@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.
Files changed (3) hide show
  1. package/dist/index.js +47 -4
  2. package/index.ts +52 -4
  3. 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
- registerTaskHandler({ jobHandlerName, handler, parallelLimit = 3, }) {
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/background-jobs",
3
- "version": "1.11.7",
3
+ "version": "1.12.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",