@adminforth/background-jobs 1.11.7 → 1.12.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/dist/index.js +47 -4
- package/index.ts +52 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
17
17
|
constructor(options) {
|
|
18
18
|
super(options, import.meta.url);
|
|
19
19
|
this.taskHandlers = {};
|
|
20
|
+
this.onAllTasksDoneHandlers = {};
|
|
20
21
|
this.jobCustomComponents = {};
|
|
21
22
|
this.jobParallelLimits = {};
|
|
22
23
|
this.levelDbInstances = {};
|
|
@@ -152,10 +153,48 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
152
153
|
return count ? parseInt(count, 10) : 0;
|
|
153
154
|
});
|
|
154
155
|
}
|
|
155
|
-
|
|
156
|
+
getAllTasksDoneStatus(levelDb) {
|
|
157
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
const totalTasks = yield this.getTotalTasksInLevelDb(levelDb);
|
|
159
|
+
let failedTasks = 0;
|
|
160
|
+
let succeededTasks = 0;
|
|
161
|
+
for (let taskIndex = 0; taskIndex < totalTasks; taskIndex++) {
|
|
162
|
+
const status = yield this.getLevelDbTaskStatusField(levelDb, taskIndex.toString());
|
|
163
|
+
if (status === 'FAILED') {
|
|
164
|
+
failedTasks++;
|
|
165
|
+
}
|
|
166
|
+
else if (status === 'DONE') {
|
|
167
|
+
succeededTasks++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { failedTasks, succeededTasks };
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
triggerOnAllTasksDone(onAllTasksDone, levelDb, jobId) {
|
|
174
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
175
|
+
if (!onAllTasksDone) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const status = yield this.getAllTasksDoneStatus(levelDb);
|
|
180
|
+
yield onAllTasksDone(Object.assign({ jobId }, status));
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
184
|
+
afLogger.error(`Error in onAllTasksDone callback for job ${jobId}: ${errorMessage}`);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
registerTaskHandler({ jobHandlerName, handler, parallelLimit = 3, onAllTasksDone, }) {
|
|
156
189
|
//register the handler in a map with jobHandlerName as key and handler as value
|
|
157
190
|
this.taskHandlers[jobHandlerName] = handler;
|
|
158
191
|
this.jobParallelLimits[jobHandlerName] = parallelLimit;
|
|
192
|
+
if (onAllTasksDone) {
|
|
193
|
+
this.onAllTasksDoneHandlers[jobHandlerName] = onAllTasksDone;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
delete this.onAllTasksDoneHandlers[jobHandlerName];
|
|
197
|
+
}
|
|
159
198
|
}
|
|
160
199
|
registerTaskDetailsComponent({ jobHandlerName, component, }) {
|
|
161
200
|
this.jobCustomComponents[jobHandlerName] = component;
|
|
@@ -163,6 +202,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
163
202
|
startNewJob(jobName, adminUser, tasks, jobHandlerName) {
|
|
164
203
|
return __awaiter(this, void 0, void 0, function* () {
|
|
165
204
|
const handleTask = this.taskHandlers[jobHandlerName];
|
|
205
|
+
const onAllTasksDone = this.onAllTasksDoneHandlers[jobHandlerName];
|
|
166
206
|
if (!handleTask) {
|
|
167
207
|
throw new Error(`No handler registered for jobHandler ${jobHandlerName}. Please register a handler using the registerTaskHandler method before starting a job with this jobHandler.`);
|
|
168
208
|
}
|
|
@@ -203,11 +243,11 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
203
243
|
return limit2(() => this.createLevelDbTaskRecord(jobLevelDb, index.toString(), task.state));
|
|
204
244
|
});
|
|
205
245
|
yield Promise.all(createTaskRecordsPromises);
|
|
206
|
-
this.runProcessingTasks(tasks, jobLevelDb, jobId, handleTask, parrallelLimit);
|
|
246
|
+
this.runProcessingTasks(tasks, jobLevelDb, jobId, handleTask, parrallelLimit, onAllTasksDone);
|
|
207
247
|
return jobId;
|
|
208
248
|
});
|
|
209
249
|
}
|
|
210
|
-
runProcessingTasks(tasks, jobLevelDb, jobId, handleTask, parrallelLimit) {
|
|
250
|
+
runProcessingTasks(tasks, jobLevelDb, jobId, handleTask, parrallelLimit, onAllTasksDone) {
|
|
211
251
|
return __awaiter(this, void 0, void 0, function* () {
|
|
212
252
|
const totalTasks = tasks.length;
|
|
213
253
|
let completedTasks = 0;
|
|
@@ -277,6 +317,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
277
317
|
});
|
|
278
318
|
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE', finishedAt: (new Date()).toISOString() });
|
|
279
319
|
this.cleanupJobMutexIfTerminalStatus(jobId, 'DONE');
|
|
320
|
+
yield this.triggerOnAllTasksDone(onAllTasksDone, jobLevelDb, jobId);
|
|
280
321
|
}
|
|
281
322
|
else if (failedTasks > 0) {
|
|
282
323
|
yield this.adminforth.resource(this.getResourceId()).update(jobId, {
|
|
@@ -285,6 +326,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
285
326
|
});
|
|
286
327
|
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE_WITH_ERRORS' });
|
|
287
328
|
this.cleanupJobMutexIfTerminalStatus(jobId, 'DONE_WITH_ERRORS');
|
|
329
|
+
yield this.triggerOnAllTasksDone(onAllTasksDone, jobLevelDb, jobId);
|
|
288
330
|
}
|
|
289
331
|
});
|
|
290
332
|
}
|
|
@@ -321,6 +363,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
321
363
|
return;
|
|
322
364
|
}
|
|
323
365
|
const parrallelLimit = this.jobParallelLimits[jobHandlerName] || 3;
|
|
366
|
+
const onAllTasksDone = this.onAllTasksDoneHandlers[jobHandlerName];
|
|
324
367
|
const unfinishedTasks = [];
|
|
325
368
|
let taskIndex = 0;
|
|
326
369
|
while (true) {
|
|
@@ -345,7 +388,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
345
388
|
}
|
|
346
389
|
taskIndex++;
|
|
347
390
|
}
|
|
348
|
-
yield this.runProcessingTasks(unfinishedTasks, jobLevelDb, job[this.getResourcePk()], handleTask, parrallelLimit);
|
|
391
|
+
yield this.runProcessingTasks(unfinishedTasks, jobLevelDb, job[this.getResourcePk()], handleTask, parrallelLimit, onAllTasksDone);
|
|
349
392
|
});
|
|
350
393
|
}
|
|
351
394
|
setJobField(jobId, key, value) {
|
package/index.ts
CHANGED
|
@@ -11,6 +11,12 @@ type TaskStatus = 'SCHEDULED' | 'IN_PROGRESS' | 'DONE' | 'FAILED';
|
|
|
11
11
|
type setStateFieldParams = (state: Record<string, any>) => void;
|
|
12
12
|
type getStateFieldParams = () => any;
|
|
13
13
|
type taskHandlerType = ( { jobId, setTaskStateField, getTaskStateField }: { jobId: string; setTaskStateField: setStateFieldParams; getTaskStateField: getStateFieldParams } ) => Promise<void>;
|
|
14
|
+
type allTasksDoneStatusType = {
|
|
15
|
+
jobId: string;
|
|
16
|
+
failedTasks: number;
|
|
17
|
+
succeededTasks: number;
|
|
18
|
+
};
|
|
19
|
+
type onAllTasksDoneType = (status: allTasksDoneStatusType) => Promise<void> | void;
|
|
14
20
|
type taskType = {
|
|
15
21
|
skip?: boolean;
|
|
16
22
|
state: Record<string, any>;
|
|
@@ -19,6 +25,7 @@ type taskType = {
|
|
|
19
25
|
export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
20
26
|
options: PluginOptions;
|
|
21
27
|
private taskHandlers: Record<string, taskHandlerType> = {};
|
|
28
|
+
private onAllTasksDoneHandlers: Partial<Record<string, onAllTasksDoneType>> = {};
|
|
22
29
|
private jobCustomComponents: Record<string, AdminForthComponentDeclarationFull> = {};
|
|
23
30
|
private jobParallelLimits: Record<string, number> = {};
|
|
24
31
|
private levelDbInstances: Record<string, Level> = {};
|
|
@@ -159,12 +166,48 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
159
166
|
const count = await levelDb.get('_meta:count');
|
|
160
167
|
return count ? parseInt(count, 10) : 0;
|
|
161
168
|
}
|
|
169
|
+
|
|
170
|
+
private async getAllTasksDoneStatus(levelDb: Level): Promise<Omit<allTasksDoneStatusType, 'jobId'>> {
|
|
171
|
+
const totalTasks = await this.getTotalTasksInLevelDb(levelDb);
|
|
172
|
+
let failedTasks = 0;
|
|
173
|
+
let succeededTasks = 0;
|
|
174
|
+
|
|
175
|
+
for (let taskIndex = 0; taskIndex < totalTasks; taskIndex++) {
|
|
176
|
+
const status = await this.getLevelDbTaskStatusField(levelDb, taskIndex.toString());
|
|
177
|
+
if (status === 'FAILED') {
|
|
178
|
+
failedTasks++;
|
|
179
|
+
} else if (status === 'DONE') {
|
|
180
|
+
succeededTasks++;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { failedTasks, succeededTasks };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private async triggerOnAllTasksDone(onAllTasksDone: onAllTasksDoneType | undefined, levelDb: Level, jobId: string) {
|
|
188
|
+
if (!onAllTasksDone) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const status = await this.getAllTasksDoneStatus(levelDb);
|
|
194
|
+
await onAllTasksDone({ jobId, ...status });
|
|
195
|
+
} catch (error) {
|
|
196
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
197
|
+
afLogger.error(`Error in onAllTasksDone callback for job ${jobId}: ${errorMessage}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
162
200
|
|
|
163
|
-
public registerTaskHandler({ jobHandlerName, handler, parallelLimit = 3,
|
|
164
|
-
}:{jobHandlerName: string, handler: taskHandlerType, parallelLimit?: number}) {
|
|
201
|
+
public registerTaskHandler({ jobHandlerName, handler, parallelLimit = 3, onAllTasksDone,
|
|
202
|
+
}:{jobHandlerName: string, handler: taskHandlerType, parallelLimit?: number, onAllTasksDone?: onAllTasksDoneType}) {
|
|
165
203
|
//register the handler in a map with jobHandlerName as key and handler as value
|
|
166
204
|
this.taskHandlers[jobHandlerName] = handler;
|
|
167
205
|
this.jobParallelLimits[jobHandlerName] = parallelLimit;
|
|
206
|
+
if (onAllTasksDone) {
|
|
207
|
+
this.onAllTasksDoneHandlers[jobHandlerName] = onAllTasksDone;
|
|
208
|
+
} else {
|
|
209
|
+
delete this.onAllTasksDoneHandlers[jobHandlerName];
|
|
210
|
+
}
|
|
168
211
|
}
|
|
169
212
|
|
|
170
213
|
public registerTaskDetailsComponent({
|
|
@@ -182,6 +225,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
182
225
|
): Promise<string> {
|
|
183
226
|
|
|
184
227
|
const handleTask: taskHandlerType = this.taskHandlers[jobHandlerName];
|
|
228
|
+
const onAllTasksDone = this.onAllTasksDoneHandlers[jobHandlerName];
|
|
185
229
|
if (!handleTask) {
|
|
186
230
|
throw new Error(`No handler registered for jobHandler ${jobHandlerName}. Please register a handler using the registerTaskHandler method before starting a job with this jobHandler.`);
|
|
187
231
|
}
|
|
@@ -226,7 +270,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
226
270
|
|
|
227
271
|
await Promise.all(createTaskRecordsPromises);
|
|
228
272
|
|
|
229
|
-
this.runProcessingTasks(tasks, jobLevelDb, jobId, handleTask, parrallelLimit);
|
|
273
|
+
this.runProcessingTasks(tasks, jobLevelDb, jobId, handleTask, parrallelLimit, onAllTasksDone);
|
|
230
274
|
return jobId;
|
|
231
275
|
}
|
|
232
276
|
|
|
@@ -236,6 +280,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
236
280
|
jobId: string,
|
|
237
281
|
handleTask: taskHandlerType,
|
|
238
282
|
parrallelLimit: number,
|
|
283
|
+
onAllTasksDone?: onAllTasksDoneType,
|
|
239
284
|
) {
|
|
240
285
|
const totalTasks = tasks.length;
|
|
241
286
|
let completedTasks = 0;
|
|
@@ -312,6 +357,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
312
357
|
})
|
|
313
358
|
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE', finishedAt: (new Date()).toISOString() });
|
|
314
359
|
this.cleanupJobMutexIfTerminalStatus(jobId, 'DONE');
|
|
360
|
+
await this.triggerOnAllTasksDone(onAllTasksDone, jobLevelDb, jobId);
|
|
315
361
|
} else if (failedTasks > 0) {
|
|
316
362
|
await this.adminforth.resource(this.getResourceId()).update(jobId, {
|
|
317
363
|
[this.options.statusField]: 'DONE_WITH_ERRORS',
|
|
@@ -319,6 +365,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
319
365
|
})
|
|
320
366
|
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE_WITH_ERRORS' });
|
|
321
367
|
this.cleanupJobMutexIfTerminalStatus(jobId, 'DONE_WITH_ERRORS');
|
|
368
|
+
await this.triggerOnAllTasksDone(onAllTasksDone, jobLevelDb, jobId);
|
|
322
369
|
}
|
|
323
370
|
}
|
|
324
371
|
|
|
@@ -355,6 +402,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
355
402
|
return;
|
|
356
403
|
}
|
|
357
404
|
const parrallelLimit = this.jobParallelLimits[jobHandlerName] || 3;
|
|
405
|
+
const onAllTasksDone = this.onAllTasksDoneHandlers[jobHandlerName];
|
|
358
406
|
|
|
359
407
|
const unfinishedTasks: taskType[] = [];
|
|
360
408
|
let taskIndex = 0;
|
|
@@ -378,7 +426,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
378
426
|
}
|
|
379
427
|
taskIndex++;
|
|
380
428
|
}
|
|
381
|
-
await this.runProcessingTasks(unfinishedTasks, jobLevelDb, job[this.getResourcePk()], handleTask, parrallelLimit);
|
|
429
|
+
await this.runProcessingTasks(unfinishedTasks, jobLevelDb, job[this.getResourcePk()], handleTask, parrallelLimit, onAllTasksDone);
|
|
382
430
|
|
|
383
431
|
}
|
|
384
432
|
|