dataiku-sdk 4.0.0 → 4.2.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 +1 -0
- package/dist/build-metadata.json +1 -1
- package/dist/packages/types/src/index.d.ts +5 -1
- package/dist/packages/types/src/index.js +5 -1
- package/dist/src/cli/command-syntax.json +4136 -2756
- package/dist/src/cli/commands/code-env.js +5 -3
- package/dist/src/cli/commands/dataset.js +6 -4
- package/dist/src/cli/commands/future.js +7 -2
- package/dist/src/cli/commands/index.js +4 -0
- package/dist/src/cli/commands/job.js +12 -9
- package/dist/src/cli/commands/messaging-channel.d.ts +17 -0
- package/dist/src/cli/commands/messaging-channel.js +385 -0
- package/dist/src/cli/commands/project-deployer.js +33 -0
- package/dist/src/cli/commands/project.js +30 -0
- package/dist/src/cli/commands/recipe.js +23 -2
- package/dist/src/cli/commands/scenario.d.ts +6 -0
- package/dist/src/cli/commands/scenario.js +495 -17
- package/dist/src/cli/contract.js +20 -4
- package/dist/src/cli/env.d.ts +6 -0
- package/dist/src/cli/env.js +8 -0
- package/dist/src/cli/flags.js +34 -0
- package/dist/src/cli/helpers/cleanup.js +11 -0
- package/dist/src/cli/list-shapes.js +5 -0
- package/dist/src/cli/output.d.ts +2 -0
- package/dist/src/cli/output.js +24 -4
- package/dist/src/cli/plans.js +94 -2
- package/dist/src/cli/read-shapes.js +92 -1
- package/dist/src/cli/result-shapes.d.ts +19 -1
- package/dist/src/cli/result-shapes.js +228 -4
- package/dist/src/cli/selector-suggestions.d.ts +14 -0
- package/dist/src/cli/selector-suggestions.js +56 -0
- package/dist/src/cli/usage.d.ts +1 -1
- package/dist/src/cli/usage.js +36 -4
- package/dist/src/cli/version.d.ts +2 -2
- package/dist/src/cli/version.js +2 -2
- package/dist/src/cli.js +62 -13
- package/dist/src/client.d.ts +16 -4
- package/dist/src/client.js +45 -9
- package/dist/src/errors.d.ts +2 -0
- package/dist/src/errors.js +12 -2
- package/dist/src/generated/action-output-schemas.json +296 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/resources/bundles.d.ts +18 -0
- package/dist/src/resources/bundles.js +26 -0
- package/dist/src/resources/futures.d.ts +3 -0
- package/dist/src/resources/futures.js +5 -1
- package/dist/src/resources/messaging-channels.d.ts +83 -0
- package/dist/src/resources/messaging-channels.js +108 -0
- package/dist/src/resources/projects.d.ts +24 -0
- package/dist/src/resources/projects.js +36 -0
- package/dist/src/resources/recipe-create.d.ts +4 -1
- package/dist/src/resources/recipe-create.js +11 -2
- package/dist/src/resources/scenario-authoring.d.ts +52 -0
- package/dist/src/resources/scenario-authoring.js +216 -0
- package/dist/src/resources/scenarios.d.ts +128 -11
- package/dist/src/resources/scenarios.js +273 -43
- package/dist/src/resources/sql.js +3 -3
- package/dist/src/utils/messaging-channel-secrets.d.ts +17 -0
- package/dist/src/utils/messaging-channel-secrets.js +114 -0
- package/package.json +2 -1
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +15 -16
- package/skills/dataiku-dss/references/discovery.md +2 -2
- package/skills/dataiku-dss/references/notifications.md +29 -0
- package/skills/dataiku-dss/references/troubleshooting.md +1 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ScenarioDetails, ScenarioStatus, ScenarioSummary, ScenarioWaitResult } from "../schemas.js";
|
|
2
2
|
import { BaseResource } from "./base.js";
|
|
3
|
-
|
|
3
|
+
import { type ScenarioListSelector } from "./scenario-authoring.js";
|
|
4
|
+
/** DSS keeps triggers and reporters at the top level of the scenario (as dataikuapi does), not under params. */
|
|
5
|
+
export declare const SCENARIO_CANONICAL_EDITABLE_FIELDS: readonly ["params.steps", "triggers", "reporters", "params.customScript", "active", "name"];
|
|
4
6
|
/** Default byte cap applied when fetching a scenario run log. */
|
|
5
7
|
export declare const DEFAULT_SCENARIO_MAX_LOG_BYTES = 1048576;
|
|
6
8
|
/** One entry of GET /scenarios/{id}/get-last-runs — a finished (or running) scenario run summary. */
|
|
@@ -44,6 +46,45 @@ export interface ScenarioRunDetails {
|
|
|
44
46
|
};
|
|
45
47
|
stepRuns?: ScenarioRunStepReport[];
|
|
46
48
|
}
|
|
49
|
+
/** One firing of a scenario trigger: what `POST /scenarios/{id}/run` returns (not a scenario run). */
|
|
50
|
+
export interface ScenarioTriggerFire {
|
|
51
|
+
triggerId: string;
|
|
52
|
+
triggerRunId: string;
|
|
53
|
+
cancelled: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** A trigger fire resolved against the scenario run it started (if any). */
|
|
56
|
+
export interface ScenarioTriggerRunState extends ScenarioTriggerFire {
|
|
57
|
+
state: "PENDING" | "RUNNING" | "FINISHED" | "CANCELLED";
|
|
58
|
+
/** The scenario run id (what get-run and log take), once DSS started the run. */
|
|
59
|
+
runId?: string;
|
|
60
|
+
outcome?: string;
|
|
61
|
+
start?: number;
|
|
62
|
+
}
|
|
63
|
+
/** Test-scenario report text (JUnit XML), byte-bounded. */
|
|
64
|
+
export interface ScenarioRunReport {
|
|
65
|
+
text: string;
|
|
66
|
+
truncated: boolean;
|
|
67
|
+
maxBytes: number;
|
|
68
|
+
}
|
|
69
|
+
export type ScenarioListKind = "trigger" | "reporter";
|
|
70
|
+
/** Outcome of adding or removing one trigger/reporter. */
|
|
71
|
+
export interface ScenarioListEdit {
|
|
72
|
+
kind: ScenarioListKind;
|
|
73
|
+
/** Position of the added element (as stored) or of the removed one. */
|
|
74
|
+
index: number;
|
|
75
|
+
/** The element as DSS stored it (added) or as it was (removed). */
|
|
76
|
+
item: unknown;
|
|
77
|
+
/** Elements left in the list. */
|
|
78
|
+
count: number;
|
|
79
|
+
}
|
|
80
|
+
export interface ScenarioRunAsResult {
|
|
81
|
+
scenarioId: string;
|
|
82
|
+
/** The explicit run-as login, or null when the scenario runs as its last editor. */
|
|
83
|
+
runAsUser: string | null;
|
|
84
|
+
/** Who the scenario actually runs as (dataikuapi `effective_run_as`). */
|
|
85
|
+
effectiveRunAs?: string;
|
|
86
|
+
previousRunAsUser?: string;
|
|
87
|
+
}
|
|
47
88
|
/** Bounded log retrieval result: text plus whether the byte cap cut it. */
|
|
48
89
|
export interface ScenarioRunLog {
|
|
49
90
|
text: string;
|
|
@@ -120,6 +161,8 @@ export interface ScenarioScriptRunResult {
|
|
|
120
161
|
/** Observable throwaway-scenario cleanup outcome; never silently swallowed here. */
|
|
121
162
|
cleanup: ScenarioScriptRunCleanup;
|
|
122
163
|
}
|
|
164
|
+
/** Who a scenario really runs as: its `runAsUser`, else the last editor (dataikuapi `effective_run_as`). */
|
|
165
|
+
export declare function effectiveRunAs(scenario: Record<string, unknown>): string | undefined;
|
|
123
166
|
export declare function normalizeScenarioUpdateData(data: Record<string, unknown>): {
|
|
124
167
|
normalizedData: Record<string, unknown>;
|
|
125
168
|
normalization: ScenarioUpdateNormalization[];
|
|
@@ -138,14 +181,23 @@ export declare class ScenariosResource extends BaseResource {
|
|
|
138
181
|
data?: Record<string, unknown>;
|
|
139
182
|
projectKey?: string;
|
|
140
183
|
}): Promise<void>;
|
|
141
|
-
/**
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Trigger a scenario run. `params` is sent as the POST body (dataikuapi
|
|
186
|
+
* `run(params)`) and reaches the run as its trigger parameters. The result
|
|
187
|
+
* identifies the TRIGGER fire, not a scenario run: resolve it with
|
|
188
|
+
* {@link getRunForTrigger}. `cancelled` is already true when DSS refused the
|
|
189
|
+
* fire because the scenario is running.
|
|
190
|
+
*/
|
|
191
|
+
run(scenarioId: string, opts?: {
|
|
192
|
+
params?: Record<string, unknown>;
|
|
193
|
+
projectKey?: string;
|
|
194
|
+
}): Promise<ScenarioTriggerFire>;
|
|
145
195
|
/** Get the light/status view of a scenario. */
|
|
146
196
|
status(scenarioId: string, projectKey?: string): Promise<ScenarioStatus>;
|
|
147
197
|
/** Merge-update a scenario's definition, then refetch and verify requested fields persisted. */
|
|
148
198
|
update(scenarioId: string, data: Record<string, unknown>, projectKey?: string): Promise<ScenarioUpdateResult>;
|
|
199
|
+
/** PUT the merge of `data` into an already-read scenario, refetch, and verify it persisted. */
|
|
200
|
+
private applyUpdate;
|
|
149
201
|
/** Delete a scenario. */
|
|
150
202
|
delete(scenarioId: string, projectKey?: string): Promise<void>;
|
|
151
203
|
/**
|
|
@@ -153,6 +205,7 @@ export declare class ScenariosResource extends BaseResource {
|
|
|
153
205
|
* Returns `{ success: false, timedOut: true }` on timeout rather than throwing.
|
|
154
206
|
*/
|
|
155
207
|
runAndWait(scenarioId: string, opts?: {
|
|
208
|
+
params?: Record<string, unknown>;
|
|
156
209
|
pollIntervalMs?: number;
|
|
157
210
|
timeoutMs?: number;
|
|
158
211
|
projectKey?: string;
|
|
@@ -177,11 +230,46 @@ export declare class ScenariosResource extends BaseResource {
|
|
|
177
230
|
* actually stop, so this does not wait for termination.
|
|
178
231
|
*/
|
|
179
232
|
abort(scenarioId: string, projectKey?: string): Promise<void>;
|
|
180
|
-
/**
|
|
233
|
+
/**
|
|
234
|
+
* Get the raw last-runs list (GET /scenarios/{id}/get-last-runs/?limit=N).
|
|
235
|
+
* `onlyFinished` drops runs still in progress (DSS `onlyFinishedRuns`).
|
|
236
|
+
*/
|
|
181
237
|
getLastRuns(scenarioId: string, opts?: {
|
|
182
238
|
limit?: number;
|
|
239
|
+
onlyFinished?: boolean;
|
|
183
240
|
projectKey?: string;
|
|
184
241
|
}): Promise<ScenarioRunSummary[]>;
|
|
242
|
+
/**
|
|
243
|
+
* Runs that started between two days (GET /scenarios/{id}/get-runs-by-date).
|
|
244
|
+
* Dates are `YYYY-MM-DD` or ISO date-times (the time part is dropped, as
|
|
245
|
+
* dataikuapi does). DSS treats `to` as exclusive (verified live: `from=to=today`
|
|
246
|
+
* is empty and today's runs need `to=tomorrow`), so it defaults to tomorrow
|
|
247
|
+
* rather than dataikuapi's today, which would leave today's runs out.
|
|
248
|
+
*/
|
|
249
|
+
getRunsByDate(scenarioId: string, opts: {
|
|
250
|
+
from: string;
|
|
251
|
+
to?: string;
|
|
252
|
+
projectKey?: string;
|
|
253
|
+
}): Promise<ScenarioRunSummary[]>;
|
|
254
|
+
/**
|
|
255
|
+
* JUnit XML report of a test scenario run (GET /scenarios/{id}/{runId}/scenario-run-report),
|
|
256
|
+
* or of one step with `stepId` (…/step-run-report?stepId=). DSS only builds
|
|
257
|
+
* reports for scenarios marked as test; the text is byte-bounded like the log.
|
|
258
|
+
*/
|
|
259
|
+
getRunReport(scenarioId: string, runId: string, opts?: {
|
|
260
|
+
stepId?: string;
|
|
261
|
+
maxBytes?: number;
|
|
262
|
+
projectKey?: string;
|
|
263
|
+
}): Promise<ScenarioRunReport>;
|
|
264
|
+
/**
|
|
265
|
+
* Resolve a trigger run (what `run` returns) to its scenario run: the scenario
|
|
266
|
+
* run id once DSS started one, or `CANCELLED` when DSS cancelled the fire (a
|
|
267
|
+
* run already in progress suppresses it) and no run will ever exist.
|
|
268
|
+
*/
|
|
269
|
+
getRunForTrigger(scenarioId: string, triggerRunId: string, opts?: {
|
|
270
|
+
triggerId?: string;
|
|
271
|
+
projectKey?: string;
|
|
272
|
+
}): Promise<ScenarioTriggerRunState>;
|
|
185
273
|
/** Get the details of a specific run (GET /scenarios/{id}/{runId}). */
|
|
186
274
|
getRunDetails(scenarioId: string, runId: string, opts?: {
|
|
187
275
|
projectKey?: string;
|
|
@@ -216,14 +304,43 @@ export declare class ScenariosResource extends BaseResource {
|
|
|
216
304
|
setActive(scenarioId: string, active: boolean, opts?: {
|
|
217
305
|
projectKey?: string;
|
|
218
306
|
}): Promise<ScenarioActiveUpdateResult>;
|
|
307
|
+
/** The scenario's triggers or reporters, in DSS order (GET /scenarios/{id}/). */
|
|
308
|
+
listItems(kind: ScenarioListKind, scenarioId: string, opts?: {
|
|
309
|
+
projectKey?: string;
|
|
310
|
+
}): Promise<unknown[]>;
|
|
311
|
+
private getSettings;
|
|
312
|
+
/**
|
|
313
|
+
* Append a trigger or reporter. `triggers`/`reporters` are arrays, which a merge
|
|
314
|
+
* update replaces wholesale, so this reads the current list and writes it back
|
|
315
|
+
* with the new element last (then verifies the stored result).
|
|
316
|
+
*/
|
|
317
|
+
addItem(kind: ScenarioListKind, scenarioId: string, item: Record<string, unknown>, opts?: {
|
|
318
|
+
projectKey?: string;
|
|
319
|
+
}): Promise<ScenarioListEdit>;
|
|
320
|
+
/** Remove the trigger or reporter selected by `--index`/`--name`, keeping every other element. */
|
|
321
|
+
removeItem(kind: ScenarioListKind, scenarioId: string, selector: ScenarioListSelector, opts?: {
|
|
322
|
+
projectKey?: string;
|
|
323
|
+
}): Promise<ScenarioListEdit>;
|
|
324
|
+
/**
|
|
325
|
+
* Set (`user`) or clear (`null`) the scenario's `runAsUser` (admin only for a
|
|
326
|
+
* non-null value). Clearing deletes the key, which a merge update cannot do.
|
|
327
|
+
*/
|
|
328
|
+
setRunAs(scenarioId: string, user: string | null, opts?: {
|
|
329
|
+
projectKey?: string;
|
|
330
|
+
}): Promise<ScenarioRunAsResult>;
|
|
219
331
|
/**
|
|
220
|
-
* Poll get-run-for-trigger until the triggered run reports an outcome
|
|
221
|
-
* deadline elapses. Shared by runAndWait and
|
|
222
|
-
* TRIGGER run id, which differs from the actual
|
|
223
|
-
* run is resolved via get-run-for-trigger (the
|
|
224
|
-
* lastRun and would misreport a completed scenario as
|
|
332
|
+
* Poll get-run-for-trigger until the triggered run reports an outcome, DSS
|
|
333
|
+
* cancels the trigger fire, or the deadline elapses. Shared by runAndWait and
|
|
334
|
+
* runScript: POST /run/ returns a TRIGGER run id, which differs from the actual
|
|
335
|
+
* scenario run id, so the real run is resolved via get-run-for-trigger (the
|
|
336
|
+
* trigger id never matches lastRun and would misreport a completed scenario as
|
|
337
|
+
* a timeout). While no scenario run exists yet, the trigger fire itself is read:
|
|
338
|
+
* DSS cancels a fire when the scenario is already running (or a newer fire
|
|
339
|
+
* replaces it), and then no run will ever appear.
|
|
225
340
|
*/
|
|
226
341
|
private trackTriggerRun;
|
|
342
|
+
/** Whether DSS cancelled this trigger fire (GET /scenarios/trigger/{id}/{triggerId}?triggerRunId=). */
|
|
343
|
+
private isTriggerFireCancelled;
|
|
227
344
|
/**
|
|
228
345
|
* Best-effort step outcome summaries from the run report
|
|
229
346
|
* (GET /scenarios/{id}/{runId}). Never fails the wait: an unavailable
|
|
@@ -4,16 +4,20 @@ import { deepMerge, } from "../utils/deep-merge.js";
|
|
|
4
4
|
import { computeNextPollDelayMs, isRequestDeadlineError, } from "../utils/polling.js";
|
|
5
5
|
import { isRecord, } from "../utils/records.js";
|
|
6
6
|
import { BaseResource, requireArrayResponse, requireNonEmpty, } from "./base.js";
|
|
7
|
+
import { selectScenarioListIndex, } from "./scenario-authoring.js";
|
|
8
|
+
/** DSS keeps triggers and reporters at the top level of the scenario (as dataikuapi does), not under params. */
|
|
7
9
|
export const SCENARIO_CANONICAL_EDITABLE_FIELDS = [
|
|
8
10
|
"params.steps",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
+
"triggers",
|
|
12
|
+
"reporters",
|
|
11
13
|
"params.customScript",
|
|
12
14
|
"active",
|
|
13
15
|
"name",
|
|
14
16
|
];
|
|
15
17
|
/** Default byte cap applied when fetching a scenario run log. */
|
|
16
18
|
export const DEFAULT_SCENARIO_MAX_LOG_BYTES = 1_048_576;
|
|
19
|
+
/** Where each list lives on the scenario object (top level, as dataikuapi). */
|
|
20
|
+
const SCENARIO_LIST_FIELDS = { trigger: "triggers", reporter: "reporters", };
|
|
17
21
|
/**
|
|
18
22
|
* Wraps a primary code-run failure so the original error (and its taxonomy:
|
|
19
23
|
* category, exit-code semantics) stays intact while the concurrent throwaway
|
|
@@ -48,11 +52,55 @@ function triggerRunIdOf(trigger, scenarioId) {
|
|
|
48
52
|
}
|
|
49
53
|
return runId;
|
|
50
54
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const triggerId =
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
/** Trigger id (`manual` for API runs), trigger run id and cancellation flag of a POST /run response. */
|
|
56
|
+
function triggerFireOf(trigger, scenarioId) {
|
|
57
|
+
const triggerId = isRecord(trigger.trigger) && typeof trigger.trigger.id === "string"
|
|
58
|
+
? trigger.trigger.id
|
|
59
|
+
: "manual";
|
|
60
|
+
return {
|
|
61
|
+
triggerId,
|
|
62
|
+
triggerRunId: triggerRunIdOf(trigger, scenarioId),
|
|
63
|
+
cancelled: trigger.cancelled === true,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** The `triggers`/`reporters` array of a scenario object; absent means empty, any other shape is an error. */
|
|
67
|
+
function scenarioListField(scenario, kind) {
|
|
68
|
+
const items = scenario[SCENARIO_LIST_FIELDS[kind]];
|
|
69
|
+
return items === undefined ? [] : requireArrayResponse(items, `scenario ${kind}s`);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* DSS stamps `params.lastUpdate` on every temporal trigger each time the scenario
|
|
73
|
+
* is saved, so echoing the value it last stored can never be read back (the
|
|
74
|
+
* verification after a trigger edit would report every trigger as unpersisted).
|
|
75
|
+
* Omitting it lets DSS stamp afresh, as it does for the other saved triggers.
|
|
76
|
+
*/
|
|
77
|
+
function withoutServerStamp(item) {
|
|
78
|
+
if (!isRecord(item) || !isRecord(item.params) || !("lastUpdate" in item.params))
|
|
79
|
+
return item;
|
|
80
|
+
const { lastUpdate: _stamp, ...params } = item.params;
|
|
81
|
+
return { ...item, params, };
|
|
82
|
+
}
|
|
83
|
+
/** `YYYY-MM-DD` day of a date or ISO date-time (the day DSS filters runs by); anything else is a usage error. */
|
|
84
|
+
function scenarioDay(value, name) {
|
|
85
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})(?:[T ].*)?$/.exec(value);
|
|
86
|
+
if (match) {
|
|
87
|
+
const day = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
|
|
88
|
+
if (day.getUTCMonth() === Number(match[2]) - 1 && day.getUTCDate() === Number(match[3])) {
|
|
89
|
+
return `${match[1]}-${match[2]}-${match[3]}`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
throw new ClientValidationError(`${name} must be a YYYY-MM-DD date or ISO date-time, got ${JSON.stringify(value)}.`, "invalid_flag_value");
|
|
93
|
+
}
|
|
94
|
+
/** Who a scenario really runs as: its `runAsUser`, else the last editor (dataikuapi `effective_run_as`). */
|
|
95
|
+
export function effectiveRunAs(scenario) {
|
|
96
|
+
if (typeof scenario.runAsUser === "string")
|
|
97
|
+
return scenario.runAsUser;
|
|
98
|
+
for (const tag of [scenario.versionTag, scenario.creationTag,]) {
|
|
99
|
+
if (isRecord(tag) && isRecord(tag.lastModifiedBy) && typeof tag.lastModifiedBy.login === "string") {
|
|
100
|
+
return tag.lastModifiedBy.login;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
56
104
|
}
|
|
57
105
|
/** A finite number, or undefined for anything else (absent/malformed JSON fields). */
|
|
58
106
|
function optionalNumber(value) {
|
|
@@ -320,12 +368,37 @@ export function scenarioUpdatePreview(current, data) {
|
|
|
320
368
|
}
|
|
321
369
|
function scenarioUpdateVerificationError(mismatches, normalization) {
|
|
322
370
|
const mismatchPaths = mismatches.map((mismatch) => mismatch.path).join(", ");
|
|
323
|
-
|
|
371
|
+
const body = {
|
|
324
372
|
message: `Scenario update did not persist requested fields after refetch: ${mismatchPaths}`,
|
|
325
373
|
mismatches,
|
|
326
374
|
canonicalEditableFields: SCENARIO_CANONICAL_EDITABLE_FIELDS,
|
|
327
375
|
...(normalization.length > 0 ? { normalization, } : {}),
|
|
328
|
-
}
|
|
376
|
+
};
|
|
377
|
+
const error = new DataikuError(400, "Scenario Update Verification Failed", JSON.stringify(body));
|
|
378
|
+
// CLI envelopes show `details`, never the body. Only paths go there: reporter and step
|
|
379
|
+
// values can hold webhook URLs, tokens, or credentials.
|
|
380
|
+
error.details = {
|
|
381
|
+
mismatches: mismatches.map((mismatch) => ({
|
|
382
|
+
path: mismatch.path,
|
|
383
|
+
differingPaths: differingLeafPaths(mismatch.expected, mismatch.actual, mismatch.path),
|
|
384
|
+
})),
|
|
385
|
+
canonicalEditableFields: SCENARIO_CANONICAL_EDITABLE_FIELDS,
|
|
386
|
+
};
|
|
387
|
+
return error;
|
|
388
|
+
}
|
|
389
|
+
/** Paths under `path` where the sent value is missing or different in what DSS stored (e.g. `reporters.0.messaging.channelId`). */
|
|
390
|
+
function differingLeafPaths(expected, actual, path) {
|
|
391
|
+
if (Array.isArray(expected)) {
|
|
392
|
+
if (!Array.isArray(actual))
|
|
393
|
+
return [path,];
|
|
394
|
+
return expected.flatMap((item, index) => differingLeafPaths(item, actual[index], `${path}.${index}`));
|
|
395
|
+
}
|
|
396
|
+
if (isRecord(expected)) {
|
|
397
|
+
if (!isRecord(actual))
|
|
398
|
+
return [path,];
|
|
399
|
+
return Object.entries(expected).flatMap(([key, value,]) => differingLeafPaths(value, actual[key], `${path}.${key}`));
|
|
400
|
+
}
|
|
401
|
+
return jsonValueEqual(expected, actual) ? [] : [path,];
|
|
329
402
|
}
|
|
330
403
|
export class ScenariosResource extends BaseResource {
|
|
331
404
|
/** List all scenarios in a project. */
|
|
@@ -348,16 +421,24 @@ export class ScenariosResource extends BaseResource {
|
|
|
348
421
|
name,
|
|
349
422
|
projectKey: pk,
|
|
350
423
|
type: scenarioType,
|
|
351
|
-
|
|
424
|
+
...(scenarioType === "step_based"
|
|
425
|
+
? { params: { steps: [], }, triggers: [], reporters: [], }
|
|
426
|
+
: { params: {}, }),
|
|
352
427
|
...opts?.data,
|
|
353
428
|
};
|
|
354
429
|
await this.client.post(`/public/api/projects/${this.enc(opts?.projectKey)}/scenarios/`, body);
|
|
355
430
|
}
|
|
356
|
-
/**
|
|
357
|
-
|
|
431
|
+
/**
|
|
432
|
+
* Trigger a scenario run. `params` is sent as the POST body (dataikuapi
|
|
433
|
+
* `run(params)`) and reaches the run as its trigger parameters. The result
|
|
434
|
+
* identifies the TRIGGER fire, not a scenario run: resolve it with
|
|
435
|
+
* {@link getRunForTrigger}. `cancelled` is already true when DSS refused the
|
|
436
|
+
* fire because the scenario is running.
|
|
437
|
+
*/
|
|
438
|
+
async run(scenarioId, opts) {
|
|
358
439
|
const scEnc = encodeURIComponent(requireNonEmpty(scenarioId, "scenarioId"));
|
|
359
|
-
const result = await this.client.post(`/public/api/projects/${this.enc(projectKey)}/scenarios/${scEnc}/run/`, {});
|
|
360
|
-
return
|
|
440
|
+
const result = await this.client.post(`/public/api/projects/${this.enc(opts?.projectKey)}/scenarios/${scEnc}/run/`, opts?.params ?? {});
|
|
441
|
+
return triggerFireOf(result, scenarioId);
|
|
361
442
|
}
|
|
362
443
|
/** Get the light/status view of a scenario. */
|
|
363
444
|
async status(scenarioId, projectKey) {
|
|
@@ -367,12 +448,15 @@ export class ScenariosResource extends BaseResource {
|
|
|
367
448
|
}
|
|
368
449
|
/** Merge-update a scenario's definition, then refetch and verify requested fields persisted. */
|
|
369
450
|
async update(scenarioId, data, projectKey) {
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
451
|
+
const current = await this.client.get(`/public/api/projects/${this.enc(projectKey)}/scenarios/${encodeURIComponent(scenarioId)}/`);
|
|
452
|
+
return this.applyUpdate(scenarioId, current, data, projectKey);
|
|
453
|
+
}
|
|
454
|
+
/** PUT the merge of `data` into an already-read scenario, refetch, and verify it persisted. */
|
|
455
|
+
async applyUpdate(scenarioId, current, data, projectKey) {
|
|
456
|
+
const path = `/public/api/projects/${this.enc(projectKey)}/scenarios/${encodeURIComponent(scenarioId)}/`;
|
|
373
457
|
const preview = scenarioUpdatePreview(current, data);
|
|
374
|
-
await this.client.put(
|
|
375
|
-
const after = await this.client.get(
|
|
458
|
+
await this.client.put(path, preview.next);
|
|
459
|
+
const after = await this.client.get(path);
|
|
376
460
|
const mismatches = scenarioFieldMismatches(after, preview.next, preview.normalizedData);
|
|
377
461
|
if (mismatches.length > 0) {
|
|
378
462
|
throw scenarioUpdateVerificationError(mismatches, preview.normalization);
|
|
@@ -406,14 +490,15 @@ export class ScenariosResource extends BaseResource {
|
|
|
406
490
|
// POST /run/ returns a TRIGGER run id, which differs from the actual scenario
|
|
407
491
|
// run id; resolve the real run via get-run-for-trigger so a completed scenario
|
|
408
492
|
// is not misreported as a timeout (the trigger id never matches lastRun).
|
|
409
|
-
const trigger = await this.client.post(`${base}/run/`, {});
|
|
410
|
-
const
|
|
493
|
+
const trigger = await this.client.post(`${base}/run/`, opts?.params ?? {});
|
|
494
|
+
const fire = triggerFireOf(trigger, scenarioId);
|
|
495
|
+
const tracked = await this.trackTriggerRun(scenarioId, opts?.projectKey, trigger, {
|
|
411
496
|
startedAt,
|
|
412
497
|
baseIntervalMs,
|
|
413
498
|
adaptivePolling,
|
|
414
499
|
timeout,
|
|
415
500
|
});
|
|
416
|
-
const resolvedRunId = tracked.runId ||
|
|
501
|
+
const resolvedRunId = tracked.runId || fire.triggerRunId;
|
|
417
502
|
const steps = tracked.timedOut || !tracked.runId
|
|
418
503
|
? undefined
|
|
419
504
|
: await this.runStepSummaries(base, tracked.runId);
|
|
@@ -424,9 +509,10 @@ export class ScenariosResource extends BaseResource {
|
|
|
424
509
|
success: tracked.outcome === "SUCCESS",
|
|
425
510
|
elapsedMs: Date.now() - startedAt,
|
|
426
511
|
pollCount: tracked.pollCount,
|
|
427
|
-
...(
|
|
428
|
-
? { triggerRunId:
|
|
512
|
+
...(fire.triggerRunId !== resolvedRunId || tracked.cancelled
|
|
513
|
+
? { triggerRunId: fire.triggerRunId, }
|
|
429
514
|
: {}),
|
|
515
|
+
...(tracked.cancelled ? { cancelled: true, } : {}),
|
|
430
516
|
...(tracked.timedOut ? { timedOut: true, } : {}),
|
|
431
517
|
...(steps ? { steps, } : {}),
|
|
432
518
|
};
|
|
@@ -470,7 +556,7 @@ export class ScenariosResource extends BaseResource {
|
|
|
470
556
|
extension: "py",
|
|
471
557
|
});
|
|
472
558
|
const trigger = await this.client.post(`${base}/run/`, {});
|
|
473
|
-
const tracked = await this.trackTriggerRun(scenarioId,
|
|
559
|
+
const tracked = await this.trackTriggerRun(scenarioId, opts?.projectKey, trigger, {
|
|
474
560
|
startedAt,
|
|
475
561
|
baseIntervalMs,
|
|
476
562
|
adaptivePolling,
|
|
@@ -535,16 +621,81 @@ export class ScenariosResource extends BaseResource {
|
|
|
535
621
|
const scEnc = encodeURIComponent(scenarioId);
|
|
536
622
|
await this.client.post(`/public/api/projects/${this.enc(projectKey)}/scenarios/${scEnc}/abort`);
|
|
537
623
|
}
|
|
538
|
-
/**
|
|
624
|
+
/**
|
|
625
|
+
* Get the raw last-runs list (GET /scenarios/{id}/get-last-runs/?limit=N).
|
|
626
|
+
* `onlyFinished` drops runs still in progress (DSS `onlyFinishedRuns`).
|
|
627
|
+
*/
|
|
539
628
|
async getLastRuns(scenarioId, opts) {
|
|
540
629
|
if (opts?.limit !== undefined && (!Number.isInteger(opts.limit) || opts.limit < 1)) {
|
|
541
630
|
throw new ClientValidationError(`limit must be a positive integer, got ${opts.limit}.`);
|
|
542
631
|
}
|
|
543
632
|
const scEnc = encodeURIComponent(scenarioId);
|
|
544
|
-
const query =
|
|
545
|
-
|
|
633
|
+
const query = new URLSearchParams();
|
|
634
|
+
if (opts?.limit !== undefined)
|
|
635
|
+
query.set("limit", String(opts.limit));
|
|
636
|
+
if (opts?.onlyFinished === true)
|
|
637
|
+
query.set("onlyFinishedRuns", "true");
|
|
638
|
+
const suffix = query.size > 0 ? `?${query}` : "";
|
|
639
|
+
const raw = await this.client.get(`/public/api/projects/${this.enc(opts?.projectKey)}/scenarios/${scEnc}/get-last-runs/${suffix}`);
|
|
546
640
|
return requireArrayResponse(raw, "scenarios.getLastRuns").map(scenarioRunSummaryFromRaw);
|
|
547
641
|
}
|
|
642
|
+
/**
|
|
643
|
+
* Runs that started between two days (GET /scenarios/{id}/get-runs-by-date).
|
|
644
|
+
* Dates are `YYYY-MM-DD` or ISO date-times (the time part is dropped, as
|
|
645
|
+
* dataikuapi does). DSS treats `to` as exclusive (verified live: `from=to=today`
|
|
646
|
+
* is empty and today's runs need `to=tomorrow`), so it defaults to tomorrow
|
|
647
|
+
* rather than dataikuapi's today, which would leave today's runs out.
|
|
648
|
+
*/
|
|
649
|
+
async getRunsByDate(scenarioId, opts) {
|
|
650
|
+
const query = new URLSearchParams({
|
|
651
|
+
fromDate: scenarioDay(opts.from, "from"),
|
|
652
|
+
toDate: scenarioDay(opts.to ?? new Date(Date.now() + 86_400_000).toLocaleDateString("en-CA"), "to"),
|
|
653
|
+
});
|
|
654
|
+
const raw = await this.client.get(`/public/api/projects/${this.enc(opts.projectKey)}/scenarios/${encodeURIComponent(requireNonEmpty(scenarioId, "scenarioId"))}/get-runs-by-date?${query}`);
|
|
655
|
+
return requireArrayResponse(raw, "scenarios.getRunsByDate").map(scenarioRunSummaryFromRaw);
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* JUnit XML report of a test scenario run (GET /scenarios/{id}/{runId}/scenario-run-report),
|
|
659
|
+
* or of one step with `stepId` (…/step-run-report?stepId=). DSS only builds
|
|
660
|
+
* reports for scenarios marked as test; the text is byte-bounded like the log.
|
|
661
|
+
*/
|
|
662
|
+
async getRunReport(scenarioId, runId, opts) {
|
|
663
|
+
if (opts?.stepId !== undefined)
|
|
664
|
+
requireNonEmpty(opts.stepId, "stepId");
|
|
665
|
+
const maxBytes = Math.max(0, Math.floor(opts?.maxBytes ?? DEFAULT_SCENARIO_MAX_LOG_BYTES));
|
|
666
|
+
const runPath = `/public/api/projects/${this.enc(opts?.projectKey)}/scenarios/${encodeURIComponent(requireNonEmpty(scenarioId, "scenarioId"))}/${encodeURIComponent(requireNonEmpty(runId, "runId"))}`;
|
|
667
|
+
const { text, truncated, } = await this.client.getTextLimited(opts?.stepId === undefined
|
|
668
|
+
? `${runPath}/scenario-run-report`
|
|
669
|
+
: `${runPath}/step-run-report?stepId=${encodeURIComponent(opts.stepId)}`, maxBytes);
|
|
670
|
+
return { text, truncated, maxBytes, };
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Resolve a trigger run (what `run` returns) to its scenario run: the scenario
|
|
674
|
+
* run id once DSS started one, or `CANCELLED` when DSS cancelled the fire (a
|
|
675
|
+
* run already in progress suppresses it) and no run will ever exist.
|
|
676
|
+
*/
|
|
677
|
+
async getRunForTrigger(scenarioId, triggerRunId, opts) {
|
|
678
|
+
const pkEnc = this.enc(opts?.projectKey);
|
|
679
|
+
const triggerId = requireNonEmpty(opts?.triggerId ?? "manual", "triggerId");
|
|
680
|
+
const fire = { triggerId, triggerRunId: requireNonEmpty(triggerRunId, "triggerRunId"), };
|
|
681
|
+
const run = await this.client.get(`/public/api/projects/${pkEnc}/scenarios/${encodeURIComponent(requireNonEmpty(scenarioId, "scenarioId"))}/get-run-for-trigger?triggerId=${encodeURIComponent(triggerId)}&triggerRunId=${encodeURIComponent(fire.triggerRunId)}`);
|
|
682
|
+
if (isRecord(run.scenarioRun)) {
|
|
683
|
+
const outcome = isRecord(run.scenarioRun.result) && typeof run.scenarioRun.result.outcome === "string"
|
|
684
|
+
? run.scenarioRun.result.outcome
|
|
685
|
+
: undefined;
|
|
686
|
+
const start = optionalNumber(run.scenarioRun.start);
|
|
687
|
+
return {
|
|
688
|
+
...fire,
|
|
689
|
+
cancelled: false,
|
|
690
|
+
state: outcome === undefined ? "RUNNING" : "FINISHED",
|
|
691
|
+
...(typeof run.scenarioRun.runId === "string" ? { runId: run.scenarioRun.runId, } : {}),
|
|
692
|
+
...(outcome === undefined ? {} : { outcome, }),
|
|
693
|
+
...(start === undefined ? {} : { start, }),
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
const cancelled = await this.isTriggerFireCancelled(pkEnc, scenarioId, fire);
|
|
697
|
+
return { ...fire, cancelled, state: cancelled ? "CANCELLED" : "PENDING", };
|
|
698
|
+
}
|
|
548
699
|
/** Get the details of a specific run (GET /scenarios/{id}/{runId}). */
|
|
549
700
|
async getRunDetails(scenarioId, runId, opts) {
|
|
550
701
|
return this.client.get(`/public/api/projects/${this.enc(opts?.projectKey)}/scenarios/${encodeURIComponent(scenarioId)}/${encodeURIComponent(runId)}/`);
|
|
@@ -604,34 +755,108 @@ export class ScenariosResource extends BaseResource {
|
|
|
604
755
|
status: scenarioLightStatusFromRaw(after),
|
|
605
756
|
};
|
|
606
757
|
}
|
|
758
|
+
/** The scenario's triggers or reporters, in DSS order (GET /scenarios/{id}/). */
|
|
759
|
+
async listItems(kind, scenarioId, opts) {
|
|
760
|
+
return scenarioListField(await this.getSettings(scenarioId, opts?.projectKey), kind);
|
|
761
|
+
}
|
|
762
|
+
async getSettings(scenarioId, projectKey) {
|
|
763
|
+
return this.client.get(`/public/api/projects/${this.enc(projectKey)}/scenarios/${encodeURIComponent(requireNonEmpty(scenarioId, "scenarioId"))}/`);
|
|
764
|
+
}
|
|
607
765
|
/**
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
* run is resolved via get-run-for-trigger (the trigger id never matches
|
|
612
|
-
* lastRun and would misreport a completed scenario as a timeout).
|
|
766
|
+
* Append a trigger or reporter. `triggers`/`reporters` are arrays, which a merge
|
|
767
|
+
* update replaces wholesale, so this reads the current list and writes it back
|
|
768
|
+
* with the new element last (then verifies the stored result).
|
|
613
769
|
*/
|
|
614
|
-
async
|
|
615
|
-
|
|
616
|
-
const
|
|
770
|
+
async addItem(kind, scenarioId, item, opts) {
|
|
771
|
+
const current = await this.getSettings(scenarioId, opts?.projectKey);
|
|
772
|
+
const items = scenarioListField(current, kind);
|
|
773
|
+
const result = await this.applyUpdate(scenarioId, current, {
|
|
774
|
+
[SCENARIO_LIST_FIELDS[kind]]: [...items.map(withoutServerStamp), item,],
|
|
775
|
+
}, opts?.projectKey);
|
|
776
|
+
const stored = scenarioListField(result.after, kind);
|
|
777
|
+
return { kind, index: items.length, item: stored[items.length], count: stored.length, };
|
|
778
|
+
}
|
|
779
|
+
/** Remove the trigger or reporter selected by `--index`/`--name`, keeping every other element. */
|
|
780
|
+
async removeItem(kind, scenarioId, selector, opts) {
|
|
781
|
+
const current = await this.getSettings(scenarioId, opts?.projectKey);
|
|
782
|
+
const items = scenarioListField(current, kind);
|
|
783
|
+
const index = selectScenarioListIndex(items, selector, kind);
|
|
784
|
+
const result = await this.applyUpdate(scenarioId, current, {
|
|
785
|
+
[SCENARIO_LIST_FIELDS[kind]]: items.flatMap((existing, position) => position === index ? [] : [withoutServerStamp(existing),]),
|
|
786
|
+
}, opts?.projectKey);
|
|
787
|
+
return {
|
|
788
|
+
kind,
|
|
789
|
+
index,
|
|
790
|
+
item: items[index],
|
|
791
|
+
count: scenarioListField(result.after, kind).length,
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Set (`user`) or clear (`null`) the scenario's `runAsUser` (admin only for a
|
|
796
|
+
* non-null value). Clearing deletes the key, which a merge update cannot do.
|
|
797
|
+
*/
|
|
798
|
+
async setRunAs(scenarioId, user, opts) {
|
|
799
|
+
const path = `/public/api/projects/${this.enc(opts?.projectKey)}/scenarios/${encodeURIComponent(requireNonEmpty(scenarioId, "scenarioId"))}/`;
|
|
800
|
+
const current = await this.client.get(path);
|
|
801
|
+
const { runAsUser: previous, ...withoutRunAs } = current;
|
|
802
|
+
await this.client.put(path, user === null ? withoutRunAs : { ...withoutRunAs, runAsUser: user, });
|
|
803
|
+
const after = await this.client.get(path);
|
|
804
|
+
const stored = typeof after.runAsUser === "string" ? after.runAsUser : null;
|
|
805
|
+
if (stored !== user) {
|
|
806
|
+
const error = new DataikuError(400, "Scenario Update Verification Failed", `Scenario run-as did not persist after refetch: expected ${user === null ? "no runAsUser" : "the requested user"}.`);
|
|
807
|
+
error.details = { mismatches: [{ path: "runAsUser", },], };
|
|
808
|
+
throw error;
|
|
809
|
+
}
|
|
810
|
+
return {
|
|
811
|
+
scenarioId,
|
|
812
|
+
runAsUser: stored,
|
|
813
|
+
effectiveRunAs: effectiveRunAs(after),
|
|
814
|
+
...(typeof previous === "string" ? { previousRunAsUser: previous, } : {}),
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Poll get-run-for-trigger until the triggered run reports an outcome, DSS
|
|
819
|
+
* cancels the trigger fire, or the deadline elapses. Shared by runAndWait and
|
|
820
|
+
* runScript: POST /run/ returns a TRIGGER run id, which differs from the actual
|
|
821
|
+
* scenario run id, so the real run is resolved via get-run-for-trigger (the
|
|
822
|
+
* trigger id never matches lastRun and would misreport a completed scenario as
|
|
823
|
+
* a timeout). While no scenario run exists yet, the trigger fire itself is read:
|
|
824
|
+
* DSS cancels a fire when the scenario is already running (or a newer fire
|
|
825
|
+
* replaces it), and then no run will ever appear.
|
|
826
|
+
*/
|
|
827
|
+
async trackTriggerRun(scenarioId, projectKey, trigger, timing) {
|
|
828
|
+
const fire = triggerFireOf(trigger, scenarioId);
|
|
829
|
+
if (fire.cancelled) {
|
|
830
|
+
return { runId: "", outcome: "CANCELLED", timedOut: false, cancelled: true, pollCount: 0, };
|
|
831
|
+
}
|
|
832
|
+
const pkEnc = this.enc(projectKey);
|
|
833
|
+
const base = `/public/api/projects/${pkEnc}/scenarios/${encodeURIComponent(scenarioId)}`;
|
|
834
|
+
const trigQuery = `triggerId=${encodeURIComponent(fire.triggerId)}&triggerRunId=${encodeURIComponent(fire.triggerRunId)}`;
|
|
835
|
+
const remainingMs = () => Math.max(1, timing.timeout - (Date.now() - timing.startedAt));
|
|
617
836
|
let runId = "";
|
|
618
837
|
let pollCount = 0;
|
|
619
838
|
while (true) {
|
|
620
839
|
if (Date.now() - timing.startedAt >= timing.timeout) {
|
|
621
|
-
return { runId, outcome: "TIMEOUT", timedOut: true, pollCount, };
|
|
840
|
+
return { runId, outcome: "TIMEOUT", timedOut: true, cancelled: false, pollCount, };
|
|
622
841
|
}
|
|
623
842
|
pollCount += 1;
|
|
624
|
-
// Budget the per-poll
|
|
843
|
+
// Budget the per-poll GETs by the remaining wait so a stalling
|
|
625
844
|
// trigger-status endpoint cannot defeat the overall run deadline.
|
|
626
|
-
const remainingMs = Math.max(1, timing.timeout - (Date.now() - timing.startedAt));
|
|
627
845
|
let run;
|
|
846
|
+
let cancelled = false;
|
|
628
847
|
try {
|
|
629
|
-
run = await this.client.get(`${base}/get-run-for-trigger?${trigQuery}`, { timeoutMs: remainingMs, });
|
|
848
|
+
run = await this.client.get(`${base}/get-run-for-trigger?${trigQuery}`, { timeoutMs: remainingMs(), });
|
|
849
|
+
if (!isRecord(run.scenarioRun)) {
|
|
850
|
+
cancelled = await this.isTriggerFireCancelled(pkEnc, scenarioId, fire, remainingMs());
|
|
851
|
+
}
|
|
630
852
|
}
|
|
631
853
|
catch (error) {
|
|
632
854
|
if (!isRequestDeadlineError(error, timing.startedAt + timing.timeout))
|
|
633
855
|
throw error;
|
|
634
|
-
return { runId, outcome: "TIMEOUT", timedOut: true, pollCount, };
|
|
856
|
+
return { runId, outcome: "TIMEOUT", timedOut: true, cancelled: false, pollCount, };
|
|
857
|
+
}
|
|
858
|
+
if (cancelled) {
|
|
859
|
+
return { runId, outcome: "CANCELLED", timedOut: false, cancelled: true, pollCount, };
|
|
635
860
|
}
|
|
636
861
|
const scenarioRun = run.scenarioRun;
|
|
637
862
|
if (scenarioRun) {
|
|
@@ -639,7 +864,7 @@ export class ScenariosResource extends BaseResource {
|
|
|
639
864
|
const result = scenarioRun.result;
|
|
640
865
|
const finished = result?.outcome;
|
|
641
866
|
if (finished) {
|
|
642
|
-
return { runId, outcome: finished, timedOut: false, pollCount, };
|
|
867
|
+
return { runId, outcome: finished, timedOut: false, cancelled: false, pollCount, };
|
|
643
868
|
}
|
|
644
869
|
}
|
|
645
870
|
const nextDelayMs = computeNextPollDelayMs({
|
|
@@ -648,10 +873,15 @@ export class ScenariosResource extends BaseResource {
|
|
|
648
873
|
adaptiveEnabled: timing.adaptivePolling,
|
|
649
874
|
});
|
|
650
875
|
const { promise, resolve, } = Promise.withResolvers();
|
|
651
|
-
setTimeout(resolve, Math.min(nextDelayMs,
|
|
876
|
+
setTimeout(resolve, Math.min(nextDelayMs, remainingMs()));
|
|
652
877
|
await promise;
|
|
653
878
|
}
|
|
654
879
|
}
|
|
880
|
+
/** Whether DSS cancelled this trigger fire (GET /scenarios/trigger/{id}/{triggerId}?triggerRunId=). */
|
|
881
|
+
async isTriggerFireCancelled(pkEnc, scenarioId, fire, timeoutMs) {
|
|
882
|
+
const raw = await this.client.get(`/public/api/projects/${pkEnc}/scenarios/trigger/${encodeURIComponent(scenarioId)}/${encodeURIComponent(fire.triggerId)}?triggerRunId=${encodeURIComponent(fire.triggerRunId)}`, timeoutMs === undefined ? undefined : { timeoutMs, });
|
|
883
|
+
return raw.cancelled === true;
|
|
884
|
+
}
|
|
655
885
|
/**
|
|
656
886
|
* Best-effort step outcome summaries from the run report
|
|
657
887
|
* (GET /scenarios/{id}/{runId}). Never fails the wait: an unavailable
|