@adminforth/background-jobs 1.11.6 → 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/build.log +2 -2
- package/custom/JobInfoPopup.vue +14 -0
- package/dist/custom/JobInfoPopup.vue +14 -0
- package/dist/index.js +50 -5
- package/index.ts +55 -5
- package/package.json +1 -1
package/build.log
CHANGED
|
@@ -13,5 +13,5 @@ custom/tsconfig.json
|
|
|
13
13
|
custom/useBackgroundJobApi.ts
|
|
14
14
|
custom/utils.ts
|
|
15
15
|
|
|
16
|
-
sent 18,
|
|
17
|
-
total size is
|
|
16
|
+
sent 18,730 bytes received 172 bytes 37,804.00 bytes/sec
|
|
17
|
+
total size is 18,097 speedup is 0.96
|
package/custom/JobInfoPopup.vue
CHANGED
|
@@ -67,6 +67,7 @@ import { getTimeAgoString, callAdminForthApi, getCustomComponent} from '@/utils'
|
|
|
67
67
|
import { useI18n } from 'vue-i18n';
|
|
68
68
|
import StateToIcon from './StateToIcon.vue';
|
|
69
69
|
import { useAdminforth } from '@/adminforth';
|
|
70
|
+
import { watch } from 'vue';
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
const { t } = useI18n();
|
|
@@ -133,6 +134,19 @@ async function getJobTasks(limit: number = 10, offset: number = 0): Promise<{sta
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
watch(
|
|
138
|
+
() => props.job.state?.error,
|
|
139
|
+
(error) => {
|
|
140
|
+
if (error) {
|
|
141
|
+
adminforth.alert({
|
|
142
|
+
message: error,
|
|
143
|
+
variant: 'danger',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{ immediate: true }
|
|
148
|
+
);
|
|
149
|
+
|
|
136
150
|
|
|
137
151
|
|
|
138
152
|
</script>
|
|
@@ -67,6 +67,7 @@ import { getTimeAgoString, callAdminForthApi, getCustomComponent} from '@/utils'
|
|
|
67
67
|
import { useI18n } from 'vue-i18n';
|
|
68
68
|
import StateToIcon from './StateToIcon.vue';
|
|
69
69
|
import { useAdminforth } from '@/adminforth';
|
|
70
|
+
import { watch } from 'vue';
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
const { t } = useI18n();
|
|
@@ -133,6 +134,19 @@ async function getJobTasks(limit: number = 10, offset: number = 0): Promise<{sta
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
watch(
|
|
138
|
+
() => props.job.state?.error,
|
|
139
|
+
(error) => {
|
|
140
|
+
if (error) {
|
|
141
|
+
adminforth.alert({
|
|
142
|
+
message: error,
|
|
143
|
+
variant: 'danger',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{ immediate: true }
|
|
148
|
+
);
|
|
149
|
+
|
|
136
150
|
|
|
137
151
|
|
|
138
152
|
</script>
|
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;
|
|
@@ -246,7 +286,9 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
246
286
|
this.adminforth.websocket.publish(`/background-jobs-task-update/${jobId}`, { taskIndex, status: "DONE" });
|
|
247
287
|
}
|
|
248
288
|
catch (error) {
|
|
249
|
-
|
|
289
|
+
const errorMessage = (error === null || error === void 0 ? void 0 : error.message) || 'Unknown error';
|
|
290
|
+
afLogger.error(`Error in handling task ${taskIndex} of job ${jobId}: ${errorMessage}`);
|
|
291
|
+
yield this.setJobField(jobId, 'error', errorMessage);
|
|
250
292
|
yield this.setLevelDbTaskStatusField(jobLevelDb, taskIndex.toString(), 'FAILED');
|
|
251
293
|
this.adminforth.websocket.publish(`/background-jobs-task-update/${jobId}`, { taskIndex, status: "FAILED" });
|
|
252
294
|
failedTasks++;
|
|
@@ -275,6 +317,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
275
317
|
});
|
|
276
318
|
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE', finishedAt: (new Date()).toISOString() });
|
|
277
319
|
this.cleanupJobMutexIfTerminalStatus(jobId, 'DONE');
|
|
320
|
+
yield this.triggerOnAllTasksDone(onAllTasksDone, jobLevelDb, jobId);
|
|
278
321
|
}
|
|
279
322
|
else if (failedTasks > 0) {
|
|
280
323
|
yield this.adminforth.resource(this.getResourceId()).update(jobId, {
|
|
@@ -283,6 +326,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
283
326
|
});
|
|
284
327
|
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE_WITH_ERRORS' });
|
|
285
328
|
this.cleanupJobMutexIfTerminalStatus(jobId, 'DONE_WITH_ERRORS');
|
|
329
|
+
yield this.triggerOnAllTasksDone(onAllTasksDone, jobLevelDb, jobId);
|
|
286
330
|
}
|
|
287
331
|
});
|
|
288
332
|
}
|
|
@@ -319,6 +363,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
319
363
|
return;
|
|
320
364
|
}
|
|
321
365
|
const parrallelLimit = this.jobParallelLimits[jobHandlerName] || 3;
|
|
366
|
+
const onAllTasksDone = this.onAllTasksDoneHandlers[jobHandlerName];
|
|
322
367
|
const unfinishedTasks = [];
|
|
323
368
|
let taskIndex = 0;
|
|
324
369
|
while (true) {
|
|
@@ -343,7 +388,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
343
388
|
}
|
|
344
389
|
taskIndex++;
|
|
345
390
|
}
|
|
346
|
-
yield this.runProcessingTasks(unfinishedTasks, jobLevelDb, job[this.getResourcePk()], handleTask, parrallelLimit);
|
|
391
|
+
yield this.runProcessingTasks(unfinishedTasks, jobLevelDb, job[this.getResourcePk()], handleTask, parrallelLimit, onAllTasksDone);
|
|
347
392
|
});
|
|
348
393
|
}
|
|
349
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;
|
|
@@ -279,7 +324,9 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
279
324
|
await this.setLevelDbTaskStatusField(jobLevelDb, taskIndex.toString(), 'DONE');
|
|
280
325
|
this.adminforth.websocket.publish(`/background-jobs-task-update/${jobId}`, { taskIndex, status: "DONE" });
|
|
281
326
|
} catch (error) {
|
|
282
|
-
|
|
327
|
+
const errorMessage = error?.message || 'Unknown error';
|
|
328
|
+
afLogger.error(`Error in handling task ${taskIndex} of job ${jobId}: ${errorMessage}`, );
|
|
329
|
+
await this.setJobField(jobId, 'error', errorMessage);
|
|
283
330
|
await this.setLevelDbTaskStatusField(jobLevelDb, taskIndex.toString(), 'FAILED');
|
|
284
331
|
this.adminforth.websocket.publish(`/background-jobs-task-update/${jobId}`, { taskIndex, status: "FAILED" });
|
|
285
332
|
failedTasks++;
|
|
@@ -310,6 +357,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
310
357
|
})
|
|
311
358
|
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE', finishedAt: (new Date()).toISOString() });
|
|
312
359
|
this.cleanupJobMutexIfTerminalStatus(jobId, 'DONE');
|
|
360
|
+
await this.triggerOnAllTasksDone(onAllTasksDone, jobLevelDb, jobId);
|
|
313
361
|
} else if (failedTasks > 0) {
|
|
314
362
|
await this.adminforth.resource(this.getResourceId()).update(jobId, {
|
|
315
363
|
[this.options.statusField]: 'DONE_WITH_ERRORS',
|
|
@@ -317,6 +365,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
317
365
|
})
|
|
318
366
|
this.adminforth.websocket.publish('/background-jobs', { jobId, status: 'DONE_WITH_ERRORS' });
|
|
319
367
|
this.cleanupJobMutexIfTerminalStatus(jobId, 'DONE_WITH_ERRORS');
|
|
368
|
+
await this.triggerOnAllTasksDone(onAllTasksDone, jobLevelDb, jobId);
|
|
320
369
|
}
|
|
321
370
|
}
|
|
322
371
|
|
|
@@ -353,6 +402,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
353
402
|
return;
|
|
354
403
|
}
|
|
355
404
|
const parrallelLimit = this.jobParallelLimits[jobHandlerName] || 3;
|
|
405
|
+
const onAllTasksDone = this.onAllTasksDoneHandlers[jobHandlerName];
|
|
356
406
|
|
|
357
407
|
const unfinishedTasks: taskType[] = [];
|
|
358
408
|
let taskIndex = 0;
|
|
@@ -376,7 +426,7 @@ export default class BackgroundJobsPlugin extends AdminForthPlugin {
|
|
|
376
426
|
}
|
|
377
427
|
taskIndex++;
|
|
378
428
|
}
|
|
379
|
-
await this.runProcessingTasks(unfinishedTasks, jobLevelDb, job[this.getResourcePk()], handleTask, parrallelLimit);
|
|
429
|
+
await this.runProcessingTasks(unfinishedTasks, jobLevelDb, job[this.getResourcePk()], handleTask, parrallelLimit, onAllTasksDone);
|
|
380
430
|
|
|
381
431
|
}
|
|
382
432
|
|