@hatchet-dev/typescript-sdk 1.3.2 → 1.4.0-alpha.2
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/clients/dispatcher/heartbeat/heartbeat-worker.js +3 -2
- package/clients/hatchet-client/hatchet-client.d.ts +3 -5
- package/clients/hatchet-client/hatchet-client.js +11 -72
- package/clients/worker/worker.d.ts +6 -9
- package/clients/worker/worker.js +2 -164
- package/examples/fanout-worker.js +1 -2
- package/examples/manual-trigger.js +1 -1
- package/package.json +3 -2
- package/step.d.ts +13 -11
- package/step.js +44 -40
- package/util/grpc-helpers.d.ts +10 -0
- package/util/grpc-helpers.js +79 -0
- package/v1/client/admin.d.ts +64 -0
- package/v1/client/admin.js +155 -0
- package/v1/client/client.d.ts +14 -9
- package/v1/client/client.interface.d.ts +4 -2
- package/v1/client/client.js +28 -16
- package/v1/client/worker/context.d.ts +246 -0
- package/v1/client/worker/context.js +512 -0
- package/v1/client/worker/worker-internal.d.ts +62 -0
- package/v1/client/worker/worker-internal.js +703 -0
- package/v1/client/{worker.d.ts → worker/worker.d.ts} +15 -15
- package/v1/client/{worker.js → worker/worker.js} +14 -11
- package/v1/declaration.d.ts +3 -3
- package/v1/declaration.js +21 -14
- package/v1/examples/cancellations/run.js +4 -4
- package/v1/examples/cancellations/workflow.js +2 -2
- package/v1/examples/high-memory/child-worker.js +29 -0
- package/v1/examples/high-memory/parent-worker.d.ts +1 -0
- package/v1/examples/high-memory/parent-worker.js +29 -0
- package/v1/examples/high-memory/run.d.ts +1 -0
- package/v1/examples/high-memory/run.js +27 -0
- package/v1/examples/high-memory/workflow-with-child.d.ts +12 -0
- package/v1/examples/high-memory/workflow-with-child.js +48 -0
- package/v1/examples/with_timeouts/workflow.js +4 -4
- package/v1/index.d.ts +1 -1
- package/v1/index.js +1 -1
- package/v1/task.d.ts +2 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/examples/api.js +0 -61
- /package/{examples/api.d.ts → v1/examples/high-memory/child-worker.d.ts} +0 -0
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
12
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
13
|
+
var m = o[Symbol.asyncIterator], i;
|
|
14
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
15
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
16
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
17
|
+
};
|
|
18
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
19
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.V1Worker = void 0;
|
|
23
|
+
/* eslint-disable no-underscore-dangle */
|
|
24
|
+
/* eslint-disable no-nested-ternary */
|
|
25
|
+
const hatchet_error_1 = __importDefault(require("../../../util/errors/hatchet-error"));
|
|
26
|
+
const dispatcher_1 = require("../../../protoc/dispatcher");
|
|
27
|
+
const hatchet_promise_1 = __importDefault(require("../../../util/hatchet-promise/hatchet-promise"));
|
|
28
|
+
const workflows_1 = require("../../../protoc/workflows");
|
|
29
|
+
const task_1 = require("../../task");
|
|
30
|
+
const transformer_1 = require("../../conditions/transformer");
|
|
31
|
+
const step_1 = require("../../../step");
|
|
32
|
+
const context_1 = require("./context");
|
|
33
|
+
class V1Worker {
|
|
34
|
+
constructor(client, options) {
|
|
35
|
+
this.workflow_registry = [];
|
|
36
|
+
this.futures = {};
|
|
37
|
+
this.contexts = {};
|
|
38
|
+
this.registeredWorkflowPromises = [];
|
|
39
|
+
this.labels = {};
|
|
40
|
+
this.client = client;
|
|
41
|
+
this.name = this.client.config.namespace + options.name;
|
|
42
|
+
this.action_registry = {};
|
|
43
|
+
this.maxRuns = options.maxRuns;
|
|
44
|
+
this.labels = options.labels || {};
|
|
45
|
+
process.on('SIGTERM', () => this.exitGracefully(true));
|
|
46
|
+
process.on('SIGINT', () => this.exitGracefully(true));
|
|
47
|
+
this.killing = false;
|
|
48
|
+
this.handle_kill = options.handleKill === undefined ? true : options.handleKill;
|
|
49
|
+
this.logger = client.config.logger(`Worker/${this.name}`, this.client.config.log_level);
|
|
50
|
+
}
|
|
51
|
+
registerActions(workflow) {
|
|
52
|
+
var _a;
|
|
53
|
+
const newActions = workflow.steps.reduce((acc, step) => {
|
|
54
|
+
acc[`${workflow.id}:${step.name.toLowerCase()}`] = step.run;
|
|
55
|
+
return acc;
|
|
56
|
+
}, {});
|
|
57
|
+
const onFailureAction = workflow.onFailure
|
|
58
|
+
? {
|
|
59
|
+
[`${workflow.id}-on-failure:${workflow.onFailure.name}`]: workflow.onFailure.run,
|
|
60
|
+
}
|
|
61
|
+
: {};
|
|
62
|
+
this.action_registry = Object.assign(Object.assign(Object.assign({}, this.action_registry), newActions), onFailureAction);
|
|
63
|
+
this.action_registry =
|
|
64
|
+
((_a = workflow.concurrency) === null || _a === void 0 ? void 0 : _a.name) && workflow.concurrency.key
|
|
65
|
+
? Object.assign(Object.assign({}, this.action_registry), { [`${workflow.id}:${workflow.concurrency.name.toLowerCase()}`]: workflow.concurrency.key }) : Object.assign({}, this.action_registry);
|
|
66
|
+
}
|
|
67
|
+
getHandler(workflows) {
|
|
68
|
+
throw new Error('Not implemented');
|
|
69
|
+
// TODO v1
|
|
70
|
+
// for (const workflow of workflows) {
|
|
71
|
+
// const wf: Workflow = {
|
|
72
|
+
// ...workflow,
|
|
73
|
+
// id: this.client.config.namespace + workflow.id,
|
|
74
|
+
// };
|
|
75
|
+
// this.registerActions(wf);
|
|
76
|
+
// }
|
|
77
|
+
// return new WebhookHandler(this, workflows);
|
|
78
|
+
}
|
|
79
|
+
registerWebhook(webhook) {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
return this.client._v0.admin.registerWebhook(Object.assign({}, webhook));
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @deprecated use registerWorkflow instead
|
|
86
|
+
*/
|
|
87
|
+
register_workflow(initWorkflow) {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
return this.registerWorkflow(initWorkflow);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
registerDurableActionsV1(workflow) {
|
|
93
|
+
const newActions = workflow._durableTasks.reduce((acc, task) => {
|
|
94
|
+
acc[`${workflow.name}:${task.name.toLowerCase()}`] = (ctx) => task.fn(ctx.input, ctx);
|
|
95
|
+
return acc;
|
|
96
|
+
}, {});
|
|
97
|
+
this.action_registry = Object.assign(Object.assign({}, this.action_registry), newActions);
|
|
98
|
+
}
|
|
99
|
+
registerActionsV1(workflow) {
|
|
100
|
+
const newActions = workflow._tasks.reduce((acc, task) => {
|
|
101
|
+
acc[`${workflow.name}:${task.name.toLowerCase()}`] = (ctx) => task.fn(ctx.input, ctx);
|
|
102
|
+
return acc;
|
|
103
|
+
}, {});
|
|
104
|
+
const onFailureFn = workflow.onFailure
|
|
105
|
+
? typeof workflow.onFailure === 'function'
|
|
106
|
+
? workflow.onFailure
|
|
107
|
+
: workflow.onFailure.fn
|
|
108
|
+
: undefined;
|
|
109
|
+
const onFailureAction = onFailureFn
|
|
110
|
+
? {
|
|
111
|
+
[onFailureTaskName(workflow)]: (ctx) => onFailureFn(ctx.input, ctx),
|
|
112
|
+
}
|
|
113
|
+
: {};
|
|
114
|
+
this.action_registry = Object.assign(Object.assign(Object.assign({}, this.action_registry), newActions), onFailureAction);
|
|
115
|
+
}
|
|
116
|
+
registerWorkflowV1(initWorkflow) {
|
|
117
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
118
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
119
|
+
// patch the namespace
|
|
120
|
+
const workflow = Object.assign(Object.assign({}, initWorkflow.definition), { name: (this.client.config.namespace + initWorkflow.definition.name).toLowerCase() });
|
|
121
|
+
try {
|
|
122
|
+
const { concurrency } = workflow;
|
|
123
|
+
let onFailureTask;
|
|
124
|
+
if (workflow.onFailure && typeof workflow.onFailure === 'function') {
|
|
125
|
+
onFailureTask = {
|
|
126
|
+
readableId: 'on-failure-task',
|
|
127
|
+
action: onFailureTaskName(workflow),
|
|
128
|
+
timeout: '60s',
|
|
129
|
+
inputs: '{}',
|
|
130
|
+
parents: [],
|
|
131
|
+
retries: 0,
|
|
132
|
+
rateLimits: [],
|
|
133
|
+
workerLabels: {},
|
|
134
|
+
concurrency: [],
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (workflow.onFailure && typeof workflow.onFailure === 'object') {
|
|
138
|
+
const onFailure = workflow.onFailure;
|
|
139
|
+
onFailureTask = {
|
|
140
|
+
readableId: 'on-failure-task',
|
|
141
|
+
action: onFailureTaskName(workflow),
|
|
142
|
+
timeout: onFailure.executionTimeout || ((_a = workflow.taskDefaults) === null || _a === void 0 ? void 0 : _a.executionTimeout) || '60s',
|
|
143
|
+
scheduleTimeout: onFailure.scheduleTimeout || ((_b = workflow.taskDefaults) === null || _b === void 0 ? void 0 : _b.scheduleTimeout),
|
|
144
|
+
inputs: '{}',
|
|
145
|
+
parents: [],
|
|
146
|
+
retries: onFailure.retries || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.retries) || 0,
|
|
147
|
+
rateLimits: (0, step_1.mapRateLimit)(onFailure.rateLimits || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.rateLimits)),
|
|
148
|
+
workerLabels: toPbWorkerLabel(onFailure.desiredWorkerLabels || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.workerLabels)),
|
|
149
|
+
concurrency: [],
|
|
150
|
+
backoffFactor: ((_f = onFailure.backoff) === null || _f === void 0 ? void 0 : _f.factor) || ((_h = (_g = workflow.taskDefaults) === null || _g === void 0 ? void 0 : _g.backoff) === null || _h === void 0 ? void 0 : _h.factor),
|
|
151
|
+
backoffMaxSeconds: ((_j = onFailure.backoff) === null || _j === void 0 ? void 0 : _j.maxSeconds) || ((_l = (_k = workflow.taskDefaults) === null || _k === void 0 ? void 0 : _k.backoff) === null || _l === void 0 ? void 0 : _l.maxSeconds),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
let onSuccessTask;
|
|
155
|
+
if (workflow.onSuccess && typeof workflow.onSuccess === 'function') {
|
|
156
|
+
const parents = getLeaves(workflow._tasks);
|
|
157
|
+
onSuccessTask = {
|
|
158
|
+
name: 'on-success-task',
|
|
159
|
+
fn: workflow.onSuccess,
|
|
160
|
+
timeout: '60s',
|
|
161
|
+
parents,
|
|
162
|
+
retries: 0,
|
|
163
|
+
rateLimits: [],
|
|
164
|
+
desiredWorkerLabels: {},
|
|
165
|
+
concurrency: [],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (workflow.onSuccess && typeof workflow.onSuccess === 'object') {
|
|
169
|
+
const onSuccess = workflow.onSuccess;
|
|
170
|
+
const parents = getLeaves(workflow._tasks);
|
|
171
|
+
onSuccessTask = {
|
|
172
|
+
name: 'on-success-task',
|
|
173
|
+
fn: onSuccess.fn,
|
|
174
|
+
timeout: onSuccess.executionTimeout || ((_m = workflow.taskDefaults) === null || _m === void 0 ? void 0 : _m.executionTimeout) || '60s',
|
|
175
|
+
scheduleTimeout: onSuccess.scheduleTimeout || ((_o = workflow.taskDefaults) === null || _o === void 0 ? void 0 : _o.scheduleTimeout),
|
|
176
|
+
parents,
|
|
177
|
+
retries: onSuccess.retries || ((_p = workflow.taskDefaults) === null || _p === void 0 ? void 0 : _p.retries) || 0,
|
|
178
|
+
rateLimits: onSuccess.rateLimits || ((_q = workflow.taskDefaults) === null || _q === void 0 ? void 0 : _q.rateLimits),
|
|
179
|
+
desiredWorkerLabels: onSuccess.desiredWorkerLabels || ((_r = workflow.taskDefaults) === null || _r === void 0 ? void 0 : _r.workerLabels),
|
|
180
|
+
concurrency: onSuccess.concurrency || ((_s = workflow.taskDefaults) === null || _s === void 0 ? void 0 : _s.concurrency),
|
|
181
|
+
backoff: onSuccess.backoff || ((_t = workflow.taskDefaults) === null || _t === void 0 ? void 0 : _t.backoff),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (onSuccessTask) {
|
|
185
|
+
workflow._tasks.push(onSuccessTask);
|
|
186
|
+
}
|
|
187
|
+
// cron and event triggers
|
|
188
|
+
if (workflow.on) {
|
|
189
|
+
this.logger.warn(`\`on\` for event and cron triggers is deprecated and will be removed soon, use \`onEvents\` and \`onCrons\` instead for ${workflow.name}`);
|
|
190
|
+
}
|
|
191
|
+
const eventTriggers = [
|
|
192
|
+
...(workflow.onEvents || []),
|
|
193
|
+
...(((_u = workflow.on) === null || _u === void 0 ? void 0 : _u.event) ? [workflow.on.event] : []),
|
|
194
|
+
];
|
|
195
|
+
const cronTriggers = [
|
|
196
|
+
...(workflow.onCrons || []),
|
|
197
|
+
...(((_v = workflow.on) === null || _v === void 0 ? void 0 : _v.cron) ? [workflow.on.cron] : []),
|
|
198
|
+
];
|
|
199
|
+
const concurrencyArr = Array.isArray(concurrency) ? concurrency : [];
|
|
200
|
+
const concurrencySolo = !Array.isArray(concurrency) ? concurrency : undefined;
|
|
201
|
+
const registeredWorkflow = this.client._v0.admin.putWorkflowV1({
|
|
202
|
+
name: workflow.name,
|
|
203
|
+
description: workflow.description || '',
|
|
204
|
+
version: workflow.version || '',
|
|
205
|
+
eventTriggers,
|
|
206
|
+
cronTriggers,
|
|
207
|
+
sticky: workflow.sticky,
|
|
208
|
+
concurrencyArr,
|
|
209
|
+
onFailureTask,
|
|
210
|
+
defaultPriority: workflow.defaultPriority,
|
|
211
|
+
tasks: [...workflow._tasks, ...workflow._durableTasks].map((task) => {
|
|
212
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
213
|
+
return ({
|
|
214
|
+
readableId: task.name,
|
|
215
|
+
action: `${workflow.name}:${task.name}`,
|
|
216
|
+
timeout: task.executionTimeout ||
|
|
217
|
+
task.timeout ||
|
|
218
|
+
((_a = workflow.taskDefaults) === null || _a === void 0 ? void 0 : _a.executionTimeout) ||
|
|
219
|
+
'60s',
|
|
220
|
+
scheduleTimeout: task.scheduleTimeout || ((_b = workflow.taskDefaults) === null || _b === void 0 ? void 0 : _b.scheduleTimeout),
|
|
221
|
+
inputs: '{}',
|
|
222
|
+
parents: (_d = (_c = task.parents) === null || _c === void 0 ? void 0 : _c.map((p) => p.name)) !== null && _d !== void 0 ? _d : [],
|
|
223
|
+
userData: '{}',
|
|
224
|
+
retries: task.retries || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.retries) || 0,
|
|
225
|
+
rateLimits: (0, step_1.mapRateLimit)(task.rateLimits || ((_f = workflow.taskDefaults) === null || _f === void 0 ? void 0 : _f.rateLimits)),
|
|
226
|
+
workerLabels: toPbWorkerLabel(task.desiredWorkerLabels || ((_g = workflow.taskDefaults) === null || _g === void 0 ? void 0 : _g.workerLabels)),
|
|
227
|
+
backoffFactor: ((_h = task.backoff) === null || _h === void 0 ? void 0 : _h.factor) || ((_k = (_j = workflow.taskDefaults) === null || _j === void 0 ? void 0 : _j.backoff) === null || _k === void 0 ? void 0 : _k.factor),
|
|
228
|
+
backoffMaxSeconds: ((_l = task.backoff) === null || _l === void 0 ? void 0 : _l.maxSeconds) || ((_o = (_m = workflow.taskDefaults) === null || _m === void 0 ? void 0 : _m.backoff) === null || _o === void 0 ? void 0 : _o.maxSeconds),
|
|
229
|
+
conditions: (0, transformer_1.taskConditionsToPb)(task),
|
|
230
|
+
concurrency: task.concurrency
|
|
231
|
+
? Array.isArray(task.concurrency)
|
|
232
|
+
? task.concurrency
|
|
233
|
+
: [task.concurrency]
|
|
234
|
+
: ((_p = workflow.taskDefaults) === null || _p === void 0 ? void 0 : _p.concurrency)
|
|
235
|
+
? Array.isArray(workflow.taskDefaults.concurrency)
|
|
236
|
+
? workflow.taskDefaults.concurrency
|
|
237
|
+
: [workflow.taskDefaults.concurrency]
|
|
238
|
+
: [],
|
|
239
|
+
});
|
|
240
|
+
}),
|
|
241
|
+
concurrency: concurrencySolo,
|
|
242
|
+
});
|
|
243
|
+
this.registeredWorkflowPromises.push(registeredWorkflow);
|
|
244
|
+
yield registeredWorkflow;
|
|
245
|
+
this.workflow_registry.push(workflow);
|
|
246
|
+
}
|
|
247
|
+
catch (e) {
|
|
248
|
+
throw new hatchet_error_1.default(`Could not register workflow: ${e.message}`);
|
|
249
|
+
}
|
|
250
|
+
this.registerActionsV1(workflow);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
registerWorkflow(initWorkflow) {
|
|
254
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
255
|
+
var _a, _b, _c;
|
|
256
|
+
const workflow = Object.assign(Object.assign({}, initWorkflow), { id: (this.client.config.namespace + initWorkflow.id).toLowerCase() });
|
|
257
|
+
try {
|
|
258
|
+
if (((_a = workflow.concurrency) === null || _a === void 0 ? void 0 : _a.key) && workflow.concurrency.expression) {
|
|
259
|
+
throw new hatchet_error_1.default('Cannot have both key function and expression in workflow concurrency configuration');
|
|
260
|
+
}
|
|
261
|
+
const concurrency = ((_b = workflow.concurrency) === null || _b === void 0 ? void 0 : _b.name) || ((_c = workflow.concurrency) === null || _c === void 0 ? void 0 : _c.expression)
|
|
262
|
+
? {
|
|
263
|
+
action: !workflow.concurrency.expression
|
|
264
|
+
? `${workflow.id}:${workflow.concurrency.name}`
|
|
265
|
+
: undefined,
|
|
266
|
+
maxRuns: workflow.concurrency.maxRuns || 1,
|
|
267
|
+
expression: workflow.concurrency.expression,
|
|
268
|
+
limitStrategy: workflow.concurrency.limitStrategy || workflows_1.ConcurrencyLimitStrategy.CANCEL_IN_PROGRESS,
|
|
269
|
+
}
|
|
270
|
+
: undefined;
|
|
271
|
+
const onFailureJob = workflow.onFailure
|
|
272
|
+
? {
|
|
273
|
+
name: `${workflow.id}-on-failure`,
|
|
274
|
+
description: workflow.description,
|
|
275
|
+
steps: [
|
|
276
|
+
{
|
|
277
|
+
readableId: workflow.onFailure.name,
|
|
278
|
+
action: `${workflow.id}-on-failure:${workflow.onFailure.name}`,
|
|
279
|
+
timeout: workflow.onFailure.timeout || '60s',
|
|
280
|
+
inputs: '{}',
|
|
281
|
+
parents: [],
|
|
282
|
+
userData: '{}',
|
|
283
|
+
retries: workflow.onFailure.retries || 0,
|
|
284
|
+
rateLimits: (0, step_1.mapRateLimit)(workflow.onFailure.rate_limits),
|
|
285
|
+
workerLabels: {}, // no worker labels for on failure steps
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
}
|
|
289
|
+
: undefined;
|
|
290
|
+
const registeredWorkflow = this.client._v0.admin.putWorkflow({
|
|
291
|
+
name: workflow.id,
|
|
292
|
+
description: workflow.description,
|
|
293
|
+
version: workflow.version || '',
|
|
294
|
+
eventTriggers: workflow.on && workflow.on.event
|
|
295
|
+
? [this.client.config.namespace + workflow.on.event]
|
|
296
|
+
: [],
|
|
297
|
+
cronTriggers: workflow.on && workflow.on.cron ? [workflow.on.cron] : [],
|
|
298
|
+
scheduledTriggers: [],
|
|
299
|
+
concurrency,
|
|
300
|
+
scheduleTimeout: workflow.scheduleTimeout,
|
|
301
|
+
onFailureJob,
|
|
302
|
+
sticky: workflow.sticky,
|
|
303
|
+
jobs: [
|
|
304
|
+
{
|
|
305
|
+
name: workflow.id,
|
|
306
|
+
description: workflow.description,
|
|
307
|
+
steps: workflow.steps.map((step) => {
|
|
308
|
+
var _a, _b, _c;
|
|
309
|
+
return ({
|
|
310
|
+
readableId: step.name,
|
|
311
|
+
action: `${workflow.id}:${step.name}`,
|
|
312
|
+
timeout: step.timeout || '60s',
|
|
313
|
+
inputs: '{}',
|
|
314
|
+
parents: (_a = step.parents) !== null && _a !== void 0 ? _a : [],
|
|
315
|
+
userData: '{}',
|
|
316
|
+
retries: step.retries || 0,
|
|
317
|
+
rateLimits: (0, step_1.mapRateLimit)(step.rate_limits),
|
|
318
|
+
workerLabels: toPbWorkerLabel(step.worker_labels),
|
|
319
|
+
backoffFactor: (_b = step.backoff) === null || _b === void 0 ? void 0 : _b.factor,
|
|
320
|
+
backoffMaxSeconds: (_c = step.backoff) === null || _c === void 0 ? void 0 : _c.maxSeconds,
|
|
321
|
+
});
|
|
322
|
+
}),
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
});
|
|
326
|
+
this.registeredWorkflowPromises.push(registeredWorkflow);
|
|
327
|
+
yield registeredWorkflow;
|
|
328
|
+
this.workflow_registry.push(workflow);
|
|
329
|
+
}
|
|
330
|
+
catch (e) {
|
|
331
|
+
throw new hatchet_error_1.default(`Could not register workflow: ${e.message}`);
|
|
332
|
+
}
|
|
333
|
+
this.registerActions(workflow);
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
registerAction(actionId, action) {
|
|
337
|
+
this.action_registry[actionId.toLowerCase()] = action;
|
|
338
|
+
}
|
|
339
|
+
handleStartStepRun(action) {
|
|
340
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
341
|
+
const { actionId } = action;
|
|
342
|
+
try {
|
|
343
|
+
// Note: we always use a DurableContext since its a superset of the Context class
|
|
344
|
+
const context = new context_1.DurableContext(action, this.client, this);
|
|
345
|
+
this.contexts[action.stepRunId] = context;
|
|
346
|
+
const step = this.action_registry[actionId];
|
|
347
|
+
if (!step) {
|
|
348
|
+
this.logger.error(`Registered actions: '${Object.keys(this.action_registry).join(', ')}'`);
|
|
349
|
+
this.logger.error(`Could not find step '${actionId}'`);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const run = () => __awaiter(this, void 0, void 0, function* () {
|
|
353
|
+
return step(context);
|
|
354
|
+
});
|
|
355
|
+
const success = (result) => __awaiter(this, void 0, void 0, function* () {
|
|
356
|
+
this.logger.info(`Step run ${action.stepRunId} succeeded`);
|
|
357
|
+
try {
|
|
358
|
+
// Send the action event to the dispatcher
|
|
359
|
+
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_COMPLETED, false, result || null, action.retryCount);
|
|
360
|
+
yield this.client._v0.dispatcher.sendStepActionEvent(event);
|
|
361
|
+
}
|
|
362
|
+
catch (actionEventError) {
|
|
363
|
+
this.logger.error(`Could not send completed action event: ${actionEventError.message || actionEventError}`);
|
|
364
|
+
// send a failure event
|
|
365
|
+
const failureEvent = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, false, actionEventError.message, action.retryCount);
|
|
366
|
+
try {
|
|
367
|
+
yield this.client._v0.dispatcher.sendStepActionEvent(failureEvent);
|
|
368
|
+
}
|
|
369
|
+
catch (failureEventError) {
|
|
370
|
+
this.logger.error(`Could not send failed action event: ${failureEventError.message || failureEventError}`);
|
|
371
|
+
}
|
|
372
|
+
this.logger.error(`Could not send action event: ${actionEventError.message || actionEventError}`);
|
|
373
|
+
}
|
|
374
|
+
finally {
|
|
375
|
+
// delete the run from the futures
|
|
376
|
+
delete this.futures[action.stepRunId];
|
|
377
|
+
delete this.contexts[action.stepRunId];
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
const failure = (error) => __awaiter(this, void 0, void 0, function* () {
|
|
381
|
+
this.logger.error(`Step run ${action.stepRunId} failed: ${error.message}`);
|
|
382
|
+
if (error.stack) {
|
|
383
|
+
this.logger.error(error.stack);
|
|
384
|
+
}
|
|
385
|
+
const shouldNotRetry = error instanceof task_1.NonRetryableError;
|
|
386
|
+
try {
|
|
387
|
+
// Send the action event to the dispatcher
|
|
388
|
+
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, shouldNotRetry, {
|
|
389
|
+
message: error === null || error === void 0 ? void 0 : error.message,
|
|
390
|
+
stack: error === null || error === void 0 ? void 0 : error.stack,
|
|
391
|
+
}, action.retryCount);
|
|
392
|
+
yield this.client._v0.dispatcher.sendStepActionEvent(event);
|
|
393
|
+
}
|
|
394
|
+
catch (e) {
|
|
395
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
396
|
+
}
|
|
397
|
+
finally {
|
|
398
|
+
// delete the run from the futures
|
|
399
|
+
delete this.futures[action.stepRunId];
|
|
400
|
+
delete this.contexts[action.stepRunId];
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
const future = new hatchet_promise_1.default((() => __awaiter(this, void 0, void 0, function* () {
|
|
404
|
+
let result;
|
|
405
|
+
try {
|
|
406
|
+
result = yield run();
|
|
407
|
+
}
|
|
408
|
+
catch (e) {
|
|
409
|
+
yield failure(e);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
yield success(result);
|
|
413
|
+
}))());
|
|
414
|
+
this.futures[action.stepRunId] = future;
|
|
415
|
+
// Send the action event to the dispatcher
|
|
416
|
+
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_STARTED, false, undefined, action.retryCount);
|
|
417
|
+
this.client._v0.dispatcher.sendStepActionEvent(event).catch((e) => {
|
|
418
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
419
|
+
});
|
|
420
|
+
try {
|
|
421
|
+
yield future.promise;
|
|
422
|
+
}
|
|
423
|
+
catch (e) {
|
|
424
|
+
this.logger.error('Could not wait for step run to finish: ', e);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
catch (e) {
|
|
428
|
+
this.logger.error('Could not send action event (outer): ', e);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
handleStartGroupKeyRun(action) {
|
|
433
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
434
|
+
const { actionId } = action;
|
|
435
|
+
this.logger.error('Concurrency Key Functions have been deprecated and will be removed in a future release. Use Concurrency Expressions instead.');
|
|
436
|
+
try {
|
|
437
|
+
const context = new context_1.Context(action, this.client, this);
|
|
438
|
+
const key = action.getGroupKeyRunId;
|
|
439
|
+
if (!key) {
|
|
440
|
+
this.logger.error(`No group key run id provided for action ${actionId}`);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
this.contexts[key] = context;
|
|
444
|
+
this.logger.debug(`Starting group key run ${key}`);
|
|
445
|
+
const step = this.action_registry[actionId];
|
|
446
|
+
if (!step) {
|
|
447
|
+
this.logger.error(`Could not find step '${actionId}'`);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const run = () => __awaiter(this, void 0, void 0, function* () {
|
|
451
|
+
return step(context);
|
|
452
|
+
});
|
|
453
|
+
const success = (result) => {
|
|
454
|
+
this.logger.info(`Step run ${action.stepRunId} succeeded`);
|
|
455
|
+
try {
|
|
456
|
+
// Send the action event to the dispatcher
|
|
457
|
+
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_COMPLETED, result);
|
|
458
|
+
this.client._v0.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
459
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
catch (e) {
|
|
463
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
464
|
+
}
|
|
465
|
+
finally {
|
|
466
|
+
// delete the run from the futures
|
|
467
|
+
delete this.futures[key];
|
|
468
|
+
delete this.contexts[key];
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
const failure = (error) => {
|
|
472
|
+
this.logger.error(`Step run ${key} failed: ${error.message}`);
|
|
473
|
+
try {
|
|
474
|
+
// Send the action event to the dispatcher
|
|
475
|
+
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_FAILED, error);
|
|
476
|
+
this.client._v0.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
477
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
catch (e) {
|
|
481
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
482
|
+
}
|
|
483
|
+
finally {
|
|
484
|
+
// delete the run from the futures
|
|
485
|
+
delete this.futures[key];
|
|
486
|
+
delete this.contexts[key];
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
const future = new hatchet_promise_1.default(run().then(success).catch(failure));
|
|
490
|
+
this.futures[key] = future;
|
|
491
|
+
// Send the action event to the dispatcher
|
|
492
|
+
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_STARTED);
|
|
493
|
+
this.client._v0.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
494
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
495
|
+
});
|
|
496
|
+
yield future.promise;
|
|
497
|
+
}
|
|
498
|
+
catch (e) {
|
|
499
|
+
this.logger.error(`Could not send action event: ${e.message}`);
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
getStepActionEvent(action, eventType, shouldNotRetry, payload = '', retryCount = 0) {
|
|
504
|
+
return {
|
|
505
|
+
workerId: this.name,
|
|
506
|
+
jobId: action.jobId,
|
|
507
|
+
jobRunId: action.jobRunId,
|
|
508
|
+
stepId: action.stepId,
|
|
509
|
+
stepRunId: action.stepRunId,
|
|
510
|
+
actionId: action.actionId,
|
|
511
|
+
eventTimestamp: new Date(),
|
|
512
|
+
eventType,
|
|
513
|
+
eventPayload: JSON.stringify(payload),
|
|
514
|
+
shouldNotRetry,
|
|
515
|
+
retryCount,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
getGroupKeyActionEvent(action, eventType, payload = '') {
|
|
519
|
+
if (!action.getGroupKeyRunId) {
|
|
520
|
+
throw new hatchet_error_1.default('No group key run id provided');
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
workerId: this.name,
|
|
524
|
+
workflowRunId: action.workflowRunId,
|
|
525
|
+
getGroupKeyRunId: action.getGroupKeyRunId,
|
|
526
|
+
actionId: action.actionId,
|
|
527
|
+
eventTimestamp: new Date(),
|
|
528
|
+
eventType,
|
|
529
|
+
eventPayload: JSON.stringify(payload),
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
handleCancelStepRun(action) {
|
|
533
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
534
|
+
const { stepRunId } = action;
|
|
535
|
+
try {
|
|
536
|
+
this.logger.info(`Cancelling step run ${action.stepRunId}`);
|
|
537
|
+
const future = this.futures[stepRunId];
|
|
538
|
+
const context = this.contexts[stepRunId];
|
|
539
|
+
if (context && context.abortController) {
|
|
540
|
+
context.abortController.abort('Cancelled by worker');
|
|
541
|
+
}
|
|
542
|
+
if (future) {
|
|
543
|
+
future.promise.catch(() => {
|
|
544
|
+
this.logger.info(`Cancelled step run ${action.stepRunId}`);
|
|
545
|
+
});
|
|
546
|
+
future.cancel('Cancelled by worker');
|
|
547
|
+
yield future.promise;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
catch (e) {
|
|
551
|
+
this.logger.error('Could not cancel step run: ', e);
|
|
552
|
+
}
|
|
553
|
+
finally {
|
|
554
|
+
delete this.futures[stepRunId];
|
|
555
|
+
delete this.contexts[stepRunId];
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
stop() {
|
|
560
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
561
|
+
yield this.exitGracefully(false);
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
exitGracefully(handleKill) {
|
|
565
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
566
|
+
var _a;
|
|
567
|
+
this.killing = true;
|
|
568
|
+
this.logger.info('Starting to exit...');
|
|
569
|
+
try {
|
|
570
|
+
yield ((_a = this.listener) === null || _a === void 0 ? void 0 : _a.unregister());
|
|
571
|
+
}
|
|
572
|
+
catch (e) {
|
|
573
|
+
this.logger.error(`Could not unregister listener: ${e.message}`);
|
|
574
|
+
}
|
|
575
|
+
this.logger.info('Gracefully exiting hatchet worker, running tasks will attempt to finish...');
|
|
576
|
+
// attempt to wait for futures to finish
|
|
577
|
+
yield Promise.all(Object.values(this.futures).map(({ promise }) => promise));
|
|
578
|
+
this.logger.info('Successfully finished pending tasks.');
|
|
579
|
+
if (handleKill) {
|
|
580
|
+
this.logger.info('Exiting hatchet worker...');
|
|
581
|
+
process.exit(0);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
start() {
|
|
586
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
587
|
+
var _a, e_1, _b, _c;
|
|
588
|
+
// ensure all workflows are registered
|
|
589
|
+
yield Promise.all(this.registeredWorkflowPromises);
|
|
590
|
+
if (Object.keys(this.action_registry).length === 0) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
try {
|
|
594
|
+
this.listener = yield this.client._v0.dispatcher.getActionListener({
|
|
595
|
+
workerName: this.name,
|
|
596
|
+
services: ['default'],
|
|
597
|
+
actions: Object.keys(this.action_registry),
|
|
598
|
+
maxRuns: this.maxRuns,
|
|
599
|
+
labels: this.labels,
|
|
600
|
+
});
|
|
601
|
+
this.workerId = this.listener.workerId;
|
|
602
|
+
const generator = this.listener.actions();
|
|
603
|
+
this.logger.info(`Worker ${this.name} listening for actions`);
|
|
604
|
+
try {
|
|
605
|
+
for (var _d = true, generator_1 = __asyncValues(generator), generator_1_1; generator_1_1 = yield generator_1.next(), _a = generator_1_1.done, !_a; _d = true) {
|
|
606
|
+
_c = generator_1_1.value;
|
|
607
|
+
_d = false;
|
|
608
|
+
const action = _c;
|
|
609
|
+
this.logger.info(`Worker ${this.name} received action ${action.actionId}:${action.actionType}`);
|
|
610
|
+
void this.handleAction(action);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
614
|
+
finally {
|
|
615
|
+
try {
|
|
616
|
+
if (!_d && !_a && (_b = generator_1.return)) yield _b.call(generator_1);
|
|
617
|
+
}
|
|
618
|
+
finally { if (e_1) throw e_1.error; }
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
catch (e) {
|
|
622
|
+
if (this.killing) {
|
|
623
|
+
this.logger.info(`Exiting worker, ignoring error: ${e.message}`);
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
this.logger.error(`Could not run worker: ${e.message}`);
|
|
627
|
+
throw new hatchet_error_1.default(`Could not run worker: ${e.message}`);
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
handleAction(action) {
|
|
632
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
633
|
+
const type = action.actionType
|
|
634
|
+
? (0, dispatcher_1.actionTypeFromJSON)(action.actionType)
|
|
635
|
+
: dispatcher_1.ActionType.START_STEP_RUN;
|
|
636
|
+
if (type === dispatcher_1.ActionType.START_STEP_RUN) {
|
|
637
|
+
yield this.handleStartStepRun(action);
|
|
638
|
+
}
|
|
639
|
+
else if (type === dispatcher_1.ActionType.CANCEL_STEP_RUN) {
|
|
640
|
+
yield this.handleCancelStepRun(action);
|
|
641
|
+
}
|
|
642
|
+
else if (type === dispatcher_1.ActionType.START_GET_GROUP_KEY) {
|
|
643
|
+
yield this.handleStartGroupKeyRun(action);
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
this.logger.error(`Worker ${this.name} received unknown action type ${type}`);
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
upsertLabels(labels) {
|
|
651
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
652
|
+
this.labels = labels;
|
|
653
|
+
if (!this.workerId) {
|
|
654
|
+
this.logger.warn('Worker not registered.');
|
|
655
|
+
return this.labels;
|
|
656
|
+
}
|
|
657
|
+
this.client._v0.dispatcher.upsertWorkerLabels(this.workerId, labels);
|
|
658
|
+
return this.labels;
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
exports.V1Worker = V1Worker;
|
|
663
|
+
function toPbWorkerLabel(in_) {
|
|
664
|
+
if (!in_) {
|
|
665
|
+
return {};
|
|
666
|
+
}
|
|
667
|
+
return Object.entries(in_).reduce((acc, [key, value]) => {
|
|
668
|
+
if (!value) {
|
|
669
|
+
return Object.assign(Object.assign({}, acc), { [key]: {
|
|
670
|
+
strValue: undefined,
|
|
671
|
+
intValue: undefined,
|
|
672
|
+
} });
|
|
673
|
+
}
|
|
674
|
+
if (typeof value === 'string') {
|
|
675
|
+
return Object.assign(Object.assign({}, acc), { [key]: {
|
|
676
|
+
strValue: value,
|
|
677
|
+
intValue: undefined,
|
|
678
|
+
} });
|
|
679
|
+
}
|
|
680
|
+
if (typeof value === 'number') {
|
|
681
|
+
return Object.assign(Object.assign({}, acc), { [key]: {
|
|
682
|
+
strValue: undefined,
|
|
683
|
+
intValue: value,
|
|
684
|
+
} });
|
|
685
|
+
}
|
|
686
|
+
return Object.assign(Object.assign({}, acc), { [key]: {
|
|
687
|
+
strValue: typeof value.value === 'string' ? value.value : undefined,
|
|
688
|
+
intValue: typeof value.value === 'number' ? value.value : undefined,
|
|
689
|
+
required: value.required,
|
|
690
|
+
weight: value.weight,
|
|
691
|
+
comparator: value.comparator,
|
|
692
|
+
} });
|
|
693
|
+
}, {});
|
|
694
|
+
}
|
|
695
|
+
function onFailureTaskName(workflow) {
|
|
696
|
+
return `${workflow.name}:on-failure-task`;
|
|
697
|
+
}
|
|
698
|
+
function getLeaves(tasks) {
|
|
699
|
+
return tasks.filter((task) => isLeafTask(task, tasks));
|
|
700
|
+
}
|
|
701
|
+
function isLeafTask(task, allTasks) {
|
|
702
|
+
return !allTasks.some((t) => { var _a; return (_a = t.parents) === null || _a === void 0 ? void 0 : _a.some((p) => p.name === task.name); });
|
|
703
|
+
}
|