@dbos-inc/dbos-sdk 3.6.5-preview → 3.6.7-preview
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/dist/src/adminserver.d.ts +26 -40
- package/dist/src/adminserver.d.ts.map +1 -1
- package/dist/src/adminserver.js +381 -276
- package/dist/src/adminserver.js.map +1 -1
- package/dist/src/cli/cli.d.ts.map +1 -1
- package/dist/src/cli/cli.js +41 -13
- package/dist/src/cli/cli.js.map +1 -1
- package/dist/src/config.d.ts +0 -5
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +1 -3
- package/dist/src/config.js.map +1 -1
- package/dist/src/context.d.ts +0 -1
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/dbos-executor.d.ts +0 -1
- package/dist/src/dbos-executor.d.ts.map +1 -1
- package/dist/src/dbos-executor.js.map +1 -1
- package/dist/src/dbos.d.ts +0 -4
- package/dist/src/dbos.d.ts.map +1 -1
- package/dist/src/dbos.js +0 -9
- package/dist/src/dbos.js.map +1 -1
- package/dist/src/system_database.d.ts +0 -2
- package/dist/src/system_database.d.ts.map +1 -1
- package/dist/src/system_database.js +147 -121
- package/dist/src/system_database.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -15
package/dist/src/adminserver.js
CHANGED
@@ -22,15 +22,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
22
22
|
__setModuleDefault(result, mod);
|
23
23
|
return result;
|
24
24
|
};
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
27
|
-
};
|
28
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
29
26
|
exports.DBOSAdminServer = exports.WorkflowQueuesMetadataUrl = exports.DeactivateUrl = exports.PerfUrl = exports.HealthUrl = exports.WorkflowRecoveryUrl = exports.WorkflowUUIDHeader = void 0;
|
30
|
-
const
|
31
|
-
const
|
32
|
-
const bodyparser_1 = require("@koa/bodyparser");
|
33
|
-
const cors_1 = __importDefault(require("@koa/cors"));
|
27
|
+
const http = __importStar(require("http"));
|
28
|
+
const url = __importStar(require("url"));
|
34
29
|
const error_1 = require("./error");
|
35
30
|
const net = __importStar(require("net"));
|
36
31
|
const perf_hooks_1 = require("perf_hooks");
|
@@ -45,46 +40,131 @@ exports.HealthUrl = '/dbos-healthz';
|
|
45
40
|
exports.PerfUrl = '/dbos-perf';
|
46
41
|
exports.DeactivateUrl = '/deactivate';
|
47
42
|
exports.WorkflowQueuesMetadataUrl = '/dbos-workflow-queues-metadata';
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
43
|
+
// Helper to parse JSON body
|
44
|
+
async function parseJsonBody(req) {
|
45
|
+
return new Promise((resolve, reject) => {
|
46
|
+
let body = '';
|
47
|
+
req.on('data', (chunk) => {
|
48
|
+
body += String(chunk);
|
49
|
+
});
|
50
|
+
req.on('end', () => {
|
51
|
+
try {
|
52
|
+
resolve(body ? JSON.parse(body) : {});
|
53
|
+
}
|
54
|
+
catch (e) {
|
55
|
+
reject(new Error('Invalid JSON'));
|
56
|
+
}
|
57
|
+
});
|
58
|
+
req.on('error', reject);
|
59
|
+
});
|
60
|
+
}
|
61
|
+
// Helper to send JSON response
|
62
|
+
function sendJson(res, statusCode, data) {
|
63
|
+
res.writeHead(statusCode, {
|
64
|
+
'Content-Type': 'application/json',
|
65
|
+
'Access-Control-Allow-Origin': '*',
|
66
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
67
|
+
'Access-Control-Allow-Headers': 'Content-Type',
|
68
|
+
});
|
69
|
+
res.end(JSON.stringify(data));
|
70
|
+
}
|
71
|
+
// Helper to send text response
|
72
|
+
function sendText(res, statusCode, text) {
|
73
|
+
res.writeHead(statusCode, {
|
74
|
+
'Content-Type': 'text/plain',
|
75
|
+
'Access-Control-Allow-Origin': '*',
|
76
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
77
|
+
'Access-Control-Allow-Headers': 'Content-Type',
|
78
|
+
});
|
79
|
+
res.end(text);
|
80
|
+
}
|
81
|
+
// Helper to send no content response
|
82
|
+
function sendNoContent(res) {
|
83
|
+
res.writeHead(204, {
|
84
|
+
'Access-Control-Allow-Origin': '*',
|
85
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
86
|
+
'Access-Control-Allow-Headers': 'Content-Type',
|
87
|
+
});
|
88
|
+
res.end();
|
89
|
+
}
|
90
|
+
// Helper to extract path params
|
91
|
+
function matchPath(pattern, pathname) {
|
92
|
+
const patternParts = pattern.split('/');
|
93
|
+
const pathParts = pathname.split('/');
|
94
|
+
if (patternParts.length !== pathParts.length)
|
95
|
+
return null;
|
96
|
+
const params = {};
|
97
|
+
for (let i = 0; i < patternParts.length; i++) {
|
98
|
+
const patternPart = patternParts[i];
|
99
|
+
const pathPart = pathParts[i];
|
100
|
+
if (patternPart.startsWith(':')) {
|
101
|
+
const paramName = patternPart.substring(1);
|
102
|
+
params[paramName] = pathPart;
|
103
|
+
}
|
104
|
+
else if (patternPart !== pathPart) {
|
105
|
+
return null;
|
106
|
+
}
|
64
107
|
}
|
108
|
+
return params;
|
109
|
+
}
|
110
|
+
class DBOSAdminServer {
|
65
111
|
static setupAdminApp(dbosExec) {
|
66
|
-
const
|
67
|
-
const adminApp = new koa_1.default();
|
68
|
-
adminApp.use((0, bodyparser_1.bodyParser)());
|
69
|
-
adminApp.use((0, cors_1.default)());
|
112
|
+
const routes = [];
|
70
113
|
// Register HTTP endpoints.
|
71
|
-
DBOSAdminServer.registerHealthEndpoint(dbosExec,
|
72
|
-
DBOSAdminServer.registerRecoveryEndpoint(dbosExec,
|
73
|
-
DBOSAdminServer.registerPerfEndpoint(dbosExec,
|
74
|
-
DBOSAdminServer.registerDeactivateEndpoint(dbosExec,
|
75
|
-
DBOSAdminServer.registerCancelWorkflowEndpoint(dbosExec,
|
76
|
-
DBOSAdminServer.registerResumeWorkflowEndpoint(dbosExec,
|
77
|
-
DBOSAdminServer.registerRestartWorkflowEndpoint(dbosExec,
|
78
|
-
DBOSAdminServer.registerQueueMetadataEndpoint(dbosExec,
|
79
|
-
DBOSAdminServer.registerListWorkflowStepsEndpoint(dbosExec,
|
80
|
-
DBOSAdminServer.registerListWorkflowsEndpoint(dbosExec,
|
81
|
-
DBOSAdminServer.registerListQueuedWorkflowsEndpoint(dbosExec,
|
82
|
-
DBOSAdminServer.registerGetWorkflowEndpoint(dbosExec,
|
83
|
-
DBOSAdminServer.registerForkWorkflowEndpoint(dbosExec,
|
84
|
-
DBOSAdminServer.registerGarbageCollectEndpoint(dbosExec,
|
85
|
-
DBOSAdminServer.registerGlobalTimeoutEndpoint(dbosExec,
|
86
|
-
|
87
|
-
|
114
|
+
DBOSAdminServer.registerHealthEndpoint(dbosExec, routes);
|
115
|
+
DBOSAdminServer.registerRecoveryEndpoint(dbosExec, routes);
|
116
|
+
DBOSAdminServer.registerPerfEndpoint(dbosExec, routes);
|
117
|
+
DBOSAdminServer.registerDeactivateEndpoint(dbosExec, routes);
|
118
|
+
DBOSAdminServer.registerCancelWorkflowEndpoint(dbosExec, routes);
|
119
|
+
DBOSAdminServer.registerResumeWorkflowEndpoint(dbosExec, routes);
|
120
|
+
DBOSAdminServer.registerRestartWorkflowEndpoint(dbosExec, routes);
|
121
|
+
DBOSAdminServer.registerQueueMetadataEndpoint(dbosExec, routes);
|
122
|
+
DBOSAdminServer.registerListWorkflowStepsEndpoint(dbosExec, routes);
|
123
|
+
DBOSAdminServer.registerListWorkflowsEndpoint(dbosExec, routes);
|
124
|
+
DBOSAdminServer.registerListQueuedWorkflowsEndpoint(dbosExec, routes);
|
125
|
+
DBOSAdminServer.registerGetWorkflowEndpoint(dbosExec, routes);
|
126
|
+
DBOSAdminServer.registerForkWorkflowEndpoint(dbosExec, routes);
|
127
|
+
DBOSAdminServer.registerGarbageCollectEndpoint(dbosExec, routes);
|
128
|
+
DBOSAdminServer.registerGlobalTimeoutEndpoint(dbosExec, routes);
|
129
|
+
// Create the HTTP server
|
130
|
+
const server = http.createServer(async (req, res) => {
|
131
|
+
// Handle CORS preflight requests
|
132
|
+
if (req.method === 'OPTIONS') {
|
133
|
+
res.writeHead(200, {
|
134
|
+
'Access-Control-Allow-Origin': '*',
|
135
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
136
|
+
'Access-Control-Allow-Headers': 'Content-Type',
|
137
|
+
'Access-Control-Max-Age': '86400',
|
138
|
+
});
|
139
|
+
res.end();
|
140
|
+
return;
|
141
|
+
}
|
142
|
+
const parsedUrl = url.parse(req.url || '', true);
|
143
|
+
const pathname = parsedUrl.pathname || '';
|
144
|
+
// Find matching route
|
145
|
+
let routeFound = false;
|
146
|
+
for (const route of routes) {
|
147
|
+
if (req.method === route.method) {
|
148
|
+
const params = matchPath(route.path, pathname);
|
149
|
+
if (params !== null) {
|
150
|
+
routeFound = true;
|
151
|
+
try {
|
152
|
+
await route.handler(req, res, params);
|
153
|
+
}
|
154
|
+
catch (error) {
|
155
|
+
dbosExec.logger.error(`Request handler error: ${String(error)}`);
|
156
|
+
sendJson(res, 500, { error: 'Internal server error' });
|
157
|
+
}
|
158
|
+
break;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
if (!routeFound) {
|
163
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
164
|
+
res.end('Not Found');
|
165
|
+
}
|
166
|
+
});
|
167
|
+
return server;
|
88
168
|
}
|
89
169
|
static async checkPortAvailabilityIPv4Ipv6(port, logger) {
|
90
170
|
try {
|
@@ -132,321 +212,346 @@ class DBOSAdminServer {
|
|
132
212
|
/**
|
133
213
|
* Health check endpoint.
|
134
214
|
*/
|
135
|
-
static registerHealthEndpoint(dbosExec,
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
215
|
+
static registerHealthEndpoint(dbosExec, routes) {
|
216
|
+
routes.push({
|
217
|
+
method: 'GET',
|
218
|
+
path: exports.HealthUrl,
|
219
|
+
handler: async (req, res) => {
|
220
|
+
sendText(res, 200, 'healthy');
|
221
|
+
return Promise.resolve();
|
222
|
+
},
|
223
|
+
});
|
142
224
|
dbosExec.logger.debug(`DBOS Server Registered Healthz GET ${exports.HealthUrl}`);
|
143
225
|
}
|
144
226
|
/**
|
145
227
|
* Register workflow queue metadata endpoint.
|
146
228
|
*/
|
147
|
-
static registerQueueMetadataEndpoint(dbosExec,
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
229
|
+
static registerQueueMetadataEndpoint(dbosExec, routes) {
|
230
|
+
routes.push({
|
231
|
+
method: 'GET',
|
232
|
+
path: exports.WorkflowQueuesMetadataUrl,
|
233
|
+
handler: async (req, res) => {
|
234
|
+
const queueDetailsArray = [];
|
235
|
+
wfqueue_1.wfQueueRunner.wfQueuesByName.forEach((q, qn) => {
|
236
|
+
queueDetailsArray.push({
|
237
|
+
name: qn,
|
238
|
+
concurrency: q.concurrency,
|
239
|
+
workerConcurrency: q.workerConcurrency,
|
240
|
+
rateLimit: q.rateLimit,
|
241
|
+
});
|
156
242
|
});
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
};
|
161
|
-
router.get(exports.WorkflowQueuesMetadataUrl, queueMetadataHandler);
|
243
|
+
sendJson(res, 200, queueDetailsArray);
|
244
|
+
return Promise.resolve();
|
245
|
+
},
|
246
|
+
});
|
162
247
|
dbosExec.logger.debug(`DBOS Server Registered Queue Metadata GET ${exports.WorkflowQueuesMetadataUrl}`);
|
163
248
|
}
|
164
249
|
/**
|
165
250
|
* Register workflow recovery endpoint.
|
166
251
|
* Receives a list of executor IDs and returns a list of workflowUUIDs.
|
167
252
|
*/
|
168
|
-
static registerRecoveryEndpoint(dbosExec,
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
253
|
+
static registerRecoveryEndpoint(dbosExec, routes) {
|
254
|
+
routes.push({
|
255
|
+
method: 'POST',
|
256
|
+
path: exports.WorkflowRecoveryUrl,
|
257
|
+
handler: async (req, res) => {
|
258
|
+
const executorIDs = (await parseJsonBody(req));
|
259
|
+
dbosExec.logger.info('Recovering workflows for executors: ' + executorIDs.toString());
|
260
|
+
const recoverHandles = await dbosExec.recoverPendingWorkflows(executorIDs);
|
261
|
+
// Return a list of workflowUUIDs being recovered.
|
262
|
+
const result = await Promise.allSettled(recoverHandles.map((i) => i.workflowID)).then((results) => results.filter((i) => i.status === 'fulfilled').map((i) => i.value));
|
263
|
+
sendJson(res, 200, result);
|
264
|
+
},
|
265
|
+
});
|
179
266
|
dbosExec.logger.debug(`DBOS Server Registered Recovery POST ${exports.WorkflowRecoveryUrl}`);
|
180
267
|
}
|
181
268
|
/**
|
182
269
|
* Register performance endpoint.
|
183
270
|
* Returns information on VM performance since last call.
|
184
271
|
*/
|
185
|
-
static
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
272
|
+
static lastELU = perf_hooks_1.performance.eventLoopUtilization();
|
273
|
+
static registerPerfEndpoint(dbosExec, routes) {
|
274
|
+
routes.push({
|
275
|
+
method: 'GET',
|
276
|
+
path: exports.PerfUrl,
|
277
|
+
handler: async (req, res) => {
|
278
|
+
const currELU = perf_hooks_1.performance.eventLoopUtilization();
|
279
|
+
const elu = perf_hooks_1.performance.eventLoopUtilization(currELU, DBOSAdminServer.lastELU);
|
280
|
+
sendJson(res, 200, elu);
|
281
|
+
DBOSAdminServer.lastELU = currELU;
|
282
|
+
return Promise.resolve();
|
283
|
+
},
|
284
|
+
});
|
195
285
|
dbosExec.logger.debug(`DBOS Server Registered Perf GET ${exports.PerfUrl}`);
|
196
286
|
}
|
197
287
|
/**
|
198
288
|
* Register Deactivate endpoint.
|
199
289
|
* Deactivate consumers so that they don't start new workflows.
|
200
|
-
*
|
201
290
|
*/
|
202
291
|
static isDeactivated = false;
|
203
|
-
static registerDeactivateEndpoint(dbosExec,
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
292
|
+
static registerDeactivateEndpoint(dbosExec, routes) {
|
293
|
+
routes.push({
|
294
|
+
method: 'GET',
|
295
|
+
path: exports.DeactivateUrl,
|
296
|
+
handler: async (req, res) => {
|
297
|
+
if (!DBOSAdminServer.isDeactivated) {
|
298
|
+
dbosExec.logger.info(`Deactivating DBOS executor ${utils_1.globalParams.executorID} with version ${utils_1.globalParams.appVersion}. This executor will complete existing workflows but will not create new workflows.`);
|
299
|
+
DBOSAdminServer.isDeactivated = true;
|
300
|
+
}
|
301
|
+
await dbosExec.deactivateEventReceivers(false);
|
302
|
+
sendText(res, 200, 'Deactivated');
|
303
|
+
},
|
304
|
+
});
|
214
305
|
dbosExec.logger.debug(`DBOS Server Registered Deactivate GET ${exports.DeactivateUrl}`);
|
215
306
|
}
|
216
|
-
static registerGarbageCollectEndpoint(dbosExec,
|
307
|
+
static registerGarbageCollectEndpoint(dbosExec, routes) {
|
217
308
|
const url = '/dbos-garbage-collect';
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
309
|
+
routes.push({
|
310
|
+
method: 'POST',
|
311
|
+
path: url,
|
312
|
+
handler: async (req, res) => {
|
313
|
+
const body = (await parseJsonBody(req));
|
314
|
+
await dbosExec.systemDatabase.garbageCollect(body.cutoff_epoch_timestamp_ms, body.rows_threshold);
|
315
|
+
sendNoContent(res);
|
316
|
+
},
|
317
|
+
});
|
224
318
|
}
|
225
|
-
static registerGlobalTimeoutEndpoint(dbosExec,
|
319
|
+
static registerGlobalTimeoutEndpoint(dbosExec, routes) {
|
226
320
|
const url = '/dbos-global-timeout';
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
321
|
+
routes.push({
|
322
|
+
method: 'POST',
|
323
|
+
path: url,
|
324
|
+
handler: async (req, res) => {
|
325
|
+
const body = (await parseJsonBody(req));
|
326
|
+
await (0, workflow_management_1.globalTimeout)(dbosExec.systemDatabase, body.cutoff_epoch_timestamp_ms);
|
327
|
+
sendNoContent(res);
|
328
|
+
},
|
329
|
+
});
|
233
330
|
}
|
234
331
|
/**
|
235
|
-
*
|
236
332
|
* Register Cancel Workflow endpoint.
|
237
333
|
* Cancels a workflow by setting its status to CANCELLED.
|
238
334
|
*/
|
239
|
-
static registerCancelWorkflowEndpoint(dbosExec,
|
335
|
+
static registerCancelWorkflowEndpoint(dbosExec, routes) {
|
240
336
|
const workflowCancelUrl = '/workflows/:workflow_id/cancel';
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
337
|
+
routes.push({
|
338
|
+
method: 'POST',
|
339
|
+
path: workflowCancelUrl,
|
340
|
+
handler: async (req, res, params) => {
|
341
|
+
const workflowId = params.workflow_id;
|
342
|
+
await dbosExec.cancelWorkflow(workflowId);
|
343
|
+
sendNoContent(res);
|
344
|
+
},
|
345
|
+
});
|
247
346
|
dbosExec.logger.debug(`DBOS Server Registered Cancel Workflow POST ${workflowCancelUrl}`);
|
248
347
|
}
|
249
348
|
/**
|
250
|
-
*
|
251
349
|
* Register Resume Workflow endpoint.
|
252
350
|
* Resume a workflow.
|
253
351
|
*/
|
254
|
-
static registerResumeWorkflowEndpoint(dbosExec,
|
352
|
+
static registerResumeWorkflowEndpoint(dbosExec, routes) {
|
255
353
|
const workflowResumeUrl = '/workflows/:workflow_id/resume';
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
errorMessage = e.message;
|
354
|
+
routes.push({
|
355
|
+
method: 'POST',
|
356
|
+
path: workflowResumeUrl,
|
357
|
+
handler: async (req, res, params) => {
|
358
|
+
const workflowId = params.workflow_id;
|
359
|
+
dbosExec.logger.info(`Resuming workflow with ID: ${workflowId}`);
|
360
|
+
try {
|
361
|
+
await dbosExec.resumeWorkflow(workflowId);
|
362
|
+
sendNoContent(res);
|
266
363
|
}
|
267
|
-
|
268
|
-
errorMessage =
|
364
|
+
catch (e) {
|
365
|
+
let errorMessage = '';
|
366
|
+
if (e instanceof error_1.DBOSError) {
|
367
|
+
errorMessage = e.message;
|
368
|
+
}
|
369
|
+
else {
|
370
|
+
errorMessage = `Unknown error`;
|
371
|
+
}
|
372
|
+
dbosExec.logger.error(`Error resuming workflow ${workflowId}: ${errorMessage}`);
|
373
|
+
sendJson(res, 500, {
|
374
|
+
error: `Error resuming workflow ${workflowId}: ${errorMessage}`,
|
375
|
+
});
|
269
376
|
}
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
error: `Error resuming workflow ${workflowId}: ${errorMessage}`,
|
274
|
-
};
|
275
|
-
return;
|
276
|
-
}
|
277
|
-
koaCtxt.status = 204;
|
278
|
-
};
|
279
|
-
router.post(workflowResumeUrl, workflowResumeHandler);
|
280
|
-
dbosExec.logger.debug(`DBOS Server Registered Cancel Workflow POST ${workflowResumeUrl}`);
|
377
|
+
},
|
378
|
+
});
|
379
|
+
dbosExec.logger.debug(`DBOS Server Registered Resume Workflow POST ${workflowResumeUrl}`);
|
281
380
|
}
|
282
381
|
/**
|
283
|
-
*
|
284
382
|
* Register Restart Workflow endpoint.
|
285
383
|
* Restart a workflow.
|
286
384
|
*/
|
287
|
-
static registerRestartWorkflowEndpoint(dbosExec,
|
288
|
-
const
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
385
|
+
static registerRestartWorkflowEndpoint(dbosExec, routes) {
|
386
|
+
const workflowRestartUrl = '/workflows/:workflow_id/restart';
|
387
|
+
routes.push({
|
388
|
+
method: 'POST',
|
389
|
+
path: workflowRestartUrl,
|
390
|
+
handler: async (req, res, params) => {
|
391
|
+
const workflowId = params.workflow_id;
|
392
|
+
dbosExec.logger.info(`Restarting workflow: ${workflowId} with a new id`);
|
393
|
+
const workflowID = await dbosExec.forkWorkflow(workflowId, 0);
|
394
|
+
sendJson(res, 200, {
|
395
|
+
workflow_id: workflowID,
|
396
|
+
});
|
397
|
+
},
|
398
|
+
});
|
399
|
+
dbosExec.logger.debug(`DBOS Server Registered Restart Workflow POST ${workflowRestartUrl}`);
|
300
400
|
}
|
301
401
|
/**
|
302
|
-
*
|
303
402
|
* Register Fork Workflow endpoint.
|
304
|
-
*
|
305
403
|
*/
|
306
|
-
static registerForkWorkflowEndpoint(dbosExec,
|
307
|
-
const
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
newWorkflowID: body.new_workflow_id,
|
318
|
-
applicationVersion: body.application_version,
|
319
|
-
timeoutMS: body.timeout_ms,
|
320
|
-
});
|
321
|
-
koaCtxt.body = {
|
322
|
-
workflow_id: workflowID,
|
323
|
-
};
|
324
|
-
}
|
325
|
-
catch (e) {
|
326
|
-
let errorMessage = '';
|
327
|
-
if (e instanceof error_1.DBOSError) {
|
328
|
-
errorMessage = e.message;
|
404
|
+
static registerForkWorkflowEndpoint(dbosExec, routes) {
|
405
|
+
const workflowForkUrl = '/workflows/:workflow_id/fork';
|
406
|
+
routes.push({
|
407
|
+
method: 'POST',
|
408
|
+
path: workflowForkUrl,
|
409
|
+
handler: async (req, res, params) => {
|
410
|
+
const workflowId = params.workflow_id;
|
411
|
+
const body = (await parseJsonBody(req));
|
412
|
+
if (body.start_step === undefined) {
|
413
|
+
sendJson(res, 400, { error: 'Missing start_step in request body' });
|
414
|
+
return;
|
329
415
|
}
|
330
|
-
|
331
|
-
|
416
|
+
dbosExec.logger.info(`Forking workflow: ${workflowId} from step ${body.start_step} with a new id`);
|
417
|
+
try {
|
418
|
+
const workflowID = await dbosExec.forkWorkflow(workflowId, body.start_step, {
|
419
|
+
newWorkflowID: body.new_workflow_id,
|
420
|
+
applicationVersion: body.application_version,
|
421
|
+
timeoutMS: body.timeout_ms,
|
422
|
+
});
|
423
|
+
sendJson(res, 200, {
|
424
|
+
workflow_id: workflowID,
|
425
|
+
});
|
332
426
|
}
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
427
|
+
catch (e) {
|
428
|
+
let errorMessage = '';
|
429
|
+
if (e instanceof error_1.DBOSError) {
|
430
|
+
errorMessage = e.message;
|
431
|
+
}
|
432
|
+
else {
|
433
|
+
errorMessage = `Unknown error`;
|
434
|
+
}
|
435
|
+
dbosExec.logger.error(`Error forking workflow ${workflowId}: ${errorMessage}`);
|
436
|
+
sendJson(res, 500, {
|
437
|
+
error: `Error forking workflow ${workflowId}: ${errorMessage}`,
|
438
|
+
});
|
439
|
+
}
|
440
|
+
},
|
441
|
+
});
|
442
|
+
dbosExec.logger.debug(`DBOS Server Registered Fork Workflow POST ${workflowForkUrl}`);
|
345
443
|
}
|
346
444
|
/**
|
347
|
-
*
|
348
445
|
* Register List Workflow Steps endpoint.
|
349
446
|
* List steps for a given workflow.
|
350
447
|
*/
|
351
|
-
static registerListWorkflowStepsEndpoint(dbosExec,
|
448
|
+
static registerListWorkflowStepsEndpoint(dbosExec, routes) {
|
352
449
|
const workflowStepsUrl = '/workflows/:workflow_id/steps';
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
450
|
+
routes.push({
|
451
|
+
method: 'GET',
|
452
|
+
path: workflowStepsUrl,
|
453
|
+
handler: async (req, res, params) => {
|
454
|
+
const workflowId = params.workflow_id;
|
455
|
+
const steps = await dbosExec.listWorkflowSteps(workflowId);
|
456
|
+
const result = steps?.map((step) => ({
|
457
|
+
function_name: step.name,
|
458
|
+
function_id: step.functionID,
|
459
|
+
output: step.output ? utils_1.DBOSJSON.stringify(step.output) : undefined,
|
460
|
+
error: step.error ? utils_1.DBOSJSON.stringify((0, serialize_error_1.serializeError)(step.error)) : undefined,
|
461
|
+
child_workflow_id: step.childWorkflowID,
|
462
|
+
}));
|
463
|
+
sendJson(res, 200, result);
|
464
|
+
},
|
465
|
+
});
|
366
466
|
dbosExec.logger.debug(`DBOS Server Registered List Workflow steps Get ${workflowStepsUrl}`);
|
367
467
|
}
|
368
468
|
/**
|
369
|
-
*
|
370
469
|
* Register List Workflows endpoint.
|
371
470
|
* List workflows with optional filtering via request body.
|
372
471
|
*/
|
373
|
-
static registerListWorkflowsEndpoint(dbosExec,
|
472
|
+
static registerListWorkflowsEndpoint(dbosExec, routes) {
|
374
473
|
const listWorkflowsUrl = '/workflows';
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
474
|
+
routes.push({
|
475
|
+
method: 'POST',
|
476
|
+
path: listWorkflowsUrl,
|
477
|
+
handler: async (req, res) => {
|
478
|
+
const body = (await parseJsonBody(req));
|
479
|
+
// Map request body keys to GetWorkflowsInput properties
|
480
|
+
const input = {
|
481
|
+
workflowIDs: body.workflow_uuids,
|
482
|
+
workflowName: body.workflow_name,
|
483
|
+
authenticatedUser: body.authenticated_user,
|
484
|
+
startTime: body.start_time,
|
485
|
+
endTime: body.end_time,
|
486
|
+
status: body.status,
|
487
|
+
applicationVersion: body.application_version,
|
488
|
+
limit: body.limit,
|
489
|
+
offset: body.offset,
|
490
|
+
sortDesc: body.sort_desc,
|
491
|
+
workflow_id_prefix: body.workflow_id_prefix,
|
492
|
+
loadInput: body.load_input ?? false,
|
493
|
+
loadOutput: body.load_output ?? false,
|
494
|
+
};
|
495
|
+
const workflows = await dbosExec.listWorkflows(input);
|
496
|
+
// Map result to the underscore format.
|
497
|
+
const result = workflows.map((wf) => new protocol.WorkflowsOutput(wf));
|
498
|
+
sendJson(res, 200, result);
|
499
|
+
},
|
500
|
+
});
|
399
501
|
dbosExec.logger.debug(`DBOS Server Registered List Workflows POST ${listWorkflowsUrl}`);
|
400
502
|
}
|
401
503
|
/**
|
402
|
-
*
|
403
504
|
* Register List Queued Workflows endpoint.
|
404
505
|
* List queued workflows with optional filtering via request body.
|
405
506
|
*/
|
406
|
-
static registerListQueuedWorkflowsEndpoint(dbosExec,
|
507
|
+
static registerListQueuedWorkflowsEndpoint(dbosExec, routes) {
|
407
508
|
const listQueuedWorkflowsUrl = '/queues';
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
509
|
+
routes.push({
|
510
|
+
method: 'POST',
|
511
|
+
path: listQueuedWorkflowsUrl,
|
512
|
+
handler: async (req, res) => {
|
513
|
+
const body = (await parseJsonBody(req));
|
514
|
+
// Map request body keys to GetQueuedWorkflowsInput properties
|
515
|
+
const input = {
|
516
|
+
workflowName: body.workflow_name,
|
517
|
+
startTime: body.start_time,
|
518
|
+
endTime: body.end_time,
|
519
|
+
status: body.status,
|
520
|
+
queueName: body.queue_name,
|
521
|
+
limit: body.limit,
|
522
|
+
offset: body.offset,
|
523
|
+
sortDesc: body.sort_desc,
|
524
|
+
loadInput: body.load_input ?? false,
|
525
|
+
};
|
526
|
+
const workflows = await dbosExec.listQueuedWorkflows(input);
|
527
|
+
// Map result to the underscore format.
|
528
|
+
const result = workflows.map((wf) => new protocol.WorkflowsOutput(wf));
|
529
|
+
sendJson(res, 200, result);
|
530
|
+
},
|
531
|
+
});
|
428
532
|
dbosExec.logger.debug(`DBOS Server Registered List Queued Workflows POST ${listQueuedWorkflowsUrl}`);
|
429
533
|
}
|
430
534
|
/**
|
431
|
-
*
|
432
535
|
* Register Get Workflow endpoint.
|
433
536
|
* Get detailed information about a specific workflow by ID.
|
434
537
|
*/
|
435
|
-
static registerGetWorkflowEndpoint(dbosExec,
|
538
|
+
static registerGetWorkflowEndpoint(dbosExec, routes) {
|
436
539
|
const getWorkflowUrl = '/workflows/:workflow_id';
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
540
|
+
routes.push({
|
541
|
+
method: 'GET',
|
542
|
+
path: getWorkflowUrl,
|
543
|
+
handler: async (req, res, params) => {
|
544
|
+
const workflowId = params.workflow_id;
|
545
|
+
const workflow = await dbosExec.getWorkflowStatus(workflowId);
|
546
|
+
if (workflow) {
|
547
|
+
const result = new protocol.WorkflowsOutput(workflow);
|
548
|
+
sendJson(res, 200, result);
|
549
|
+
}
|
550
|
+
else {
|
551
|
+
sendJson(res, 404, { error: `Workflow ${workflowId} not found` });
|
552
|
+
}
|
553
|
+
},
|
554
|
+
});
|
450
555
|
dbosExec.logger.debug(`DBOS Server Registered Get Workflow GET ${getWorkflowUrl}`);
|
451
556
|
}
|
452
557
|
}
|