agenda 4.3.0 → 5.0.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/History.md +12 -3
- package/README.md +38 -9
- package/dist/agenda/create.d.ts +2 -2
- package/dist/agenda/create.d.ts.map +1 -1
- package/dist/agenda/create.js.map +1 -1
- package/dist/agenda/define.d.ts +2 -2
- package/dist/agenda/define.d.ts.map +1 -1
- package/dist/agenda/define.js.map +1 -1
- package/dist/agenda/drain.d.ts +9 -0
- package/dist/agenda/drain.d.ts.map +1 -0
- package/dist/agenda/drain.js +46 -0
- package/dist/agenda/drain.js.map +1 -0
- package/dist/agenda/every.d.ts +2 -1
- package/dist/agenda/every.d.ts.map +1 -1
- package/dist/agenda/every.js +1 -1
- package/dist/agenda/every.js.map +1 -1
- package/dist/agenda/find-and-lock-next-job.d.ts.map +1 -1
- package/dist/agenda/find-and-lock-next-job.js +3 -4
- package/dist/agenda/find-and-lock-next-job.js.map +1 -1
- package/dist/agenda/index.d.ts +2 -0
- package/dist/agenda/index.d.ts.map +1 -1
- package/dist/agenda/index.js +2 -0
- package/dist/agenda/index.js.map +1 -1
- package/dist/agenda/now.d.ts +2 -2
- package/dist/agenda/now.d.ts.map +1 -1
- package/dist/agenda/now.js.map +1 -1
- package/dist/agenda/schedule.d.ts +3 -3
- package/dist/agenda/schedule.d.ts.map +1 -1
- package/dist/agenda/schedule.js.map +1 -1
- package/dist/job/compute-next-run-at.js +2 -2
- package/dist/job/compute-next-run-at.js.map +1 -1
- package/dist/job/index.d.ts +6 -3
- package/dist/job/index.d.ts.map +1 -1
- package/dist/job/index.js.map +1 -1
- package/lib/agenda/cancel.ts +28 -0
- package/lib/agenda/close.ts +37 -0
- package/lib/agenda/create.ts +22 -0
- package/lib/agenda/database.ts +63 -0
- package/lib/agenda/db-init.ts +45 -0
- package/lib/agenda/default-concurrency.ts +19 -0
- package/lib/agenda/default-lock-lifetime.ts +17 -0
- package/lib/agenda/default-lock-limit.ts +17 -0
- package/lib/agenda/define.ts +85 -0
- package/lib/agenda/disable.ts +28 -0
- package/lib/agenda/drain.ts +30 -0
- package/lib/agenda/enable.ts +29 -0
- package/lib/agenda/every.ts +86 -0
- package/lib/agenda/find-and-lock-next-job.ts +78 -0
- package/lib/agenda/has-mongo-protocol.ts +8 -0
- package/lib/agenda/index.ts +227 -0
- package/lib/agenda/job-processing-queue.ts +99 -0
- package/lib/agenda/jobs.ts +31 -0
- package/lib/agenda/lock-limit.ts +17 -0
- package/lib/agenda/max-concurrency.ts +20 -0
- package/lib/agenda/mongo.ts +21 -0
- package/lib/agenda/name.ts +16 -0
- package/lib/agenda/now.ts +31 -0
- package/lib/agenda/process-every.ts +18 -0
- package/lib/agenda/purge.ts +19 -0
- package/lib/agenda/save-job.ts +185 -0
- package/lib/agenda/schedule.ts +79 -0
- package/lib/agenda/sort.ts +17 -0
- package/lib/agenda/start.ts +30 -0
- package/lib/agenda/stop.ts +49 -0
- package/lib/cjs.ts +5 -0
- package/lib/index.ts +11 -0
- package/lib/job/compute-next-run-at.ts +191 -0
- package/lib/job/disable.ts +11 -0
- package/lib/job/enable.ts +11 -0
- package/lib/job/fail.ts +29 -0
- package/lib/job/index.ts +221 -0
- package/lib/job/is-running.ts +29 -0
- package/lib/job/priority.ts +11 -0
- package/lib/job/remove.ts +10 -0
- package/lib/job/repeat-at.ts +12 -0
- package/lib/job/repeat-every.ts +40 -0
- package/lib/job/run.ts +125 -0
- package/lib/job/save.ts +11 -0
- package/lib/job/schedule.ts +15 -0
- package/lib/job/set-shouldsaveresult.ts +10 -0
- package/lib/job/to-json.ts +36 -0
- package/lib/job/touch.ts +11 -0
- package/lib/job/unique.ts +18 -0
- package/lib/utils/create-job.ts +13 -0
- package/lib/utils/index.ts +3 -0
- package/lib/utils/parse-priority.ts +37 -0
- package/lib/utils/process-jobs.ts +379 -0
- package/package.json +14 -12
- package/tsconfig.json +72 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import createDebugger from "debug";
|
|
2
|
+
import { Agenda } from ".";
|
|
3
|
+
import { Job, JobAttributesData } from "../job";
|
|
4
|
+
|
|
5
|
+
const debug = createDebugger("agenda:schedule");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Schedule a job or jobs at a specific time
|
|
9
|
+
* @name Agenda#schedule
|
|
10
|
+
* @function
|
|
11
|
+
* @param when when the job gets run
|
|
12
|
+
* @param names array of job names to run
|
|
13
|
+
* @param data data to send to job
|
|
14
|
+
* @returns job or jobs created
|
|
15
|
+
*/
|
|
16
|
+
export function schedule<T extends JobAttributesData>(this: Agenda, when: string | Date, names: string, data: T): Promise<Job>;
|
|
17
|
+
export function schedule<T extends JobAttributesData>(this: Agenda, when: string | Date, names: string[], data: T): Promise<Job[]>;
|
|
18
|
+
export function schedule<T extends JobAttributesData> (
|
|
19
|
+
this: Agenda,
|
|
20
|
+
when: string | Date,
|
|
21
|
+
names: string | string[],
|
|
22
|
+
data: T,
|
|
23
|
+
): Promise<Job | Job[]> {
|
|
24
|
+
/**
|
|
25
|
+
* Internal method that creates a job with given date
|
|
26
|
+
* @param when when the job gets run
|
|
27
|
+
* @param name of job to run
|
|
28
|
+
* @param data data to send to job
|
|
29
|
+
* @returns instance of new job
|
|
30
|
+
*/
|
|
31
|
+
const createJob = async (
|
|
32
|
+
when: string | Date,
|
|
33
|
+
name: string,
|
|
34
|
+
data: any
|
|
35
|
+
): Promise<Job> => {
|
|
36
|
+
const job = this.create(name, data);
|
|
37
|
+
|
|
38
|
+
await job.schedule(when).save();
|
|
39
|
+
|
|
40
|
+
return job;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Internal helper method that calls createJob on a names array
|
|
45
|
+
* @param when when the job gets run
|
|
46
|
+
* @param names names of jobs to run
|
|
47
|
+
* @param data data to send to job
|
|
48
|
+
* @returns jobs that were created
|
|
49
|
+
*/
|
|
50
|
+
const createJobs = async (
|
|
51
|
+
when: string | Date,
|
|
52
|
+
names: string[],
|
|
53
|
+
data: any
|
|
54
|
+
): Promise<Job[]> => {
|
|
55
|
+
try {
|
|
56
|
+
const createJobList: Array<Promise<Job>> = [];
|
|
57
|
+
names.map((name) => createJobList.push(createJob(when, name, data)));
|
|
58
|
+
debug("Agenda.schedule()::createJobs() -> all jobs created successfully");
|
|
59
|
+
return Promise.all(createJobList);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
debug(
|
|
62
|
+
"Agenda.schedule()::createJobs() -> error creating one or more of the jobs"
|
|
63
|
+
);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (typeof names === "string") {
|
|
69
|
+
debug("Agenda.schedule(%s, %O, [%O], cb)", when, names);
|
|
70
|
+
return createJob(when, names, data);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (Array.isArray(names)) {
|
|
74
|
+
debug("Agenda.schedule(%s, %O, [%O])", when, names);
|
|
75
|
+
return createJobs(when, names, data);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
throw new TypeError("Name must be string or array of strings")
|
|
79
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import createDebugger from "debug";
|
|
2
|
+
import { Agenda } from ".";
|
|
3
|
+
|
|
4
|
+
const debug = createDebugger("agenda:sort");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Set the sort query for finding next job
|
|
8
|
+
* Default is { nextRunAt: 1, priority: -1 }
|
|
9
|
+
* @name Agenda#sort
|
|
10
|
+
* @function
|
|
11
|
+
* @param query sort query object for MongoDB
|
|
12
|
+
*/
|
|
13
|
+
export const sort = function (this: Agenda, query: any): Agenda {
|
|
14
|
+
debug("Agenda.sort([Object])");
|
|
15
|
+
this._sort = query;
|
|
16
|
+
return this;
|
|
17
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import createDebugger from "debug";
|
|
2
|
+
import { Agenda } from ".";
|
|
3
|
+
import { processJobs } from "../utils";
|
|
4
|
+
|
|
5
|
+
const debug = createDebugger("agenda:start");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Starts processing jobs using processJobs() methods, storing an interval ID
|
|
9
|
+
* This method will only resolve if a db has been set up beforehand.
|
|
10
|
+
* @name Agenda#start
|
|
11
|
+
* @function
|
|
12
|
+
* @returns resolves if db set beforehand, returns undefined otherwise
|
|
13
|
+
*/
|
|
14
|
+
export const start = async function (this: Agenda): Promise<void | unknown> {
|
|
15
|
+
if (this._processInterval) {
|
|
16
|
+
debug("Agenda.start was already called, ignoring");
|
|
17
|
+
return this._ready;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
await this._ready;
|
|
21
|
+
debug(
|
|
22
|
+
"Agenda.start called, creating interval to call processJobs every [%dms]",
|
|
23
|
+
this._processEvery
|
|
24
|
+
);
|
|
25
|
+
this._processInterval = setInterval(
|
|
26
|
+
processJobs.bind(this),
|
|
27
|
+
this._processEvery
|
|
28
|
+
);
|
|
29
|
+
process.nextTick(processJobs.bind(this));
|
|
30
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import createDebugger from "debug";
|
|
2
|
+
import { Agenda } from ".";
|
|
3
|
+
|
|
4
|
+
const debug = createDebugger("agenda:stop");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Clear the interval that processes the jobs
|
|
8
|
+
* @name Agenda#stop
|
|
9
|
+
* @function
|
|
10
|
+
* @returns resolves when job unlocking fails or passes
|
|
11
|
+
*/
|
|
12
|
+
export const stop = async function (this: Agenda): Promise<void> {
|
|
13
|
+
/**
|
|
14
|
+
* Internal method to unlock jobs so that they can be re-run
|
|
15
|
+
* NOTE: May need to update what properties get set here, since job unlocking seems to fail
|
|
16
|
+
* @access private
|
|
17
|
+
* @returns resolves when job unlocking fails or passes
|
|
18
|
+
*/
|
|
19
|
+
const _unlockJobs = async (): Promise<void> => {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
debug("Agenda._unlockJobs()");
|
|
22
|
+
const jobIds = this._lockedJobs.map((job) => job.attrs._id);
|
|
23
|
+
|
|
24
|
+
if (jobIds.length === 0) {
|
|
25
|
+
debug("no jobs to unlock");
|
|
26
|
+
resolve();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
debug("about to unlock jobs with ids: %O", jobIds);
|
|
30
|
+
this._collection.updateMany(
|
|
31
|
+
{ _id: { $in: jobIds } },
|
|
32
|
+
{ $set: { lockedAt: null } },
|
|
33
|
+
(error) => {
|
|
34
|
+
if (error) {
|
|
35
|
+
reject(error);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this._lockedJobs = [];
|
|
39
|
+
resolve();
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
debug("Agenda.stop called, clearing interval for processJobs()");
|
|
46
|
+
clearInterval(this._processInterval);
|
|
47
|
+
this._processInterval = undefined;
|
|
48
|
+
return _unlockJobs();
|
|
49
|
+
};
|
package/lib/cjs.ts
ADDED
package/lib/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// module export, beware: cjs.ts is exported as main entry point!
|
|
2
|
+
export * from "./agenda";
|
|
3
|
+
export * from "./job";
|
|
4
|
+
|
|
5
|
+
export { DefineOptions, JobPriority, Processor } from "./agenda/define";
|
|
6
|
+
export { JobOptions } from "./job/repeat-every";
|
|
7
|
+
|
|
8
|
+
import { Agenda } from "./agenda";
|
|
9
|
+
export { Agenda };
|
|
10
|
+
|
|
11
|
+
export default Agenda;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Job } from ".";
|
|
2
|
+
import * as parser from "cron-parser";
|
|
3
|
+
import humanInterval from "human-interval";
|
|
4
|
+
import createDebugger from "debug";
|
|
5
|
+
import moment from "moment-timezone";
|
|
6
|
+
// @ts-expect-error
|
|
7
|
+
import date from "date.js";
|
|
8
|
+
|
|
9
|
+
const debug = createDebugger("agenda:job");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal method used to compute next time a job should run and sets the proper values
|
|
13
|
+
* @name Job#computeNextRunAt
|
|
14
|
+
* @function
|
|
15
|
+
*/
|
|
16
|
+
export const computeNextRunAt = function (this: Job): Job {
|
|
17
|
+
const interval = this.attrs.repeatInterval;
|
|
18
|
+
const timezone = this.attrs.repeatTimezone;
|
|
19
|
+
const { repeatAt } = this.attrs;
|
|
20
|
+
const previousNextRunAt = this.attrs.nextRunAt || new Date();
|
|
21
|
+
this.attrs.nextRunAt = undefined;
|
|
22
|
+
|
|
23
|
+
const dateForTimezone = (date: Date): moment.Moment => {
|
|
24
|
+
const mdate: moment.Moment = moment(date);
|
|
25
|
+
if (timezone) {
|
|
26
|
+
mdate.tz(timezone);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return mdate;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Internal method that computes the interval
|
|
34
|
+
*/
|
|
35
|
+
const computeFromInterval = () => {
|
|
36
|
+
debug(
|
|
37
|
+
"[%s:%s] computing next run via interval [%s]",
|
|
38
|
+
this.attrs.name,
|
|
39
|
+
this.attrs._id,
|
|
40
|
+
interval
|
|
41
|
+
);
|
|
42
|
+
const dateNow = new Date();
|
|
43
|
+
let lastRun: Date = this.attrs.lastRunAt || dateNow;
|
|
44
|
+
let { startDate, endDate, skipDays } = this.attrs;
|
|
45
|
+
lastRun = dateForTimezone(lastRun).toDate();
|
|
46
|
+
const cronOptions: any = { currentDate: lastRun };
|
|
47
|
+
if (timezone) {
|
|
48
|
+
cronOptions.tz = timezone;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
let cronTime = parser.parseExpression(interval!, cronOptions);
|
|
53
|
+
let nextDate: Date | null = cronTime.next().toDate();
|
|
54
|
+
if (
|
|
55
|
+
nextDate.getTime() === lastRun.getTime() ||
|
|
56
|
+
nextDate.getTime() <= previousNextRunAt.getTime()
|
|
57
|
+
) {
|
|
58
|
+
// Handle cronTime giving back the same date for the next run time
|
|
59
|
+
cronOptions.currentDate = new Date(lastRun.getTime() + 1000);
|
|
60
|
+
cronTime = parser.parseExpression(interval!, cronOptions);
|
|
61
|
+
nextDate = cronTime.next().toDate();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// If start date is present, check if the nextDate should be larger or equal to startDate. If not set startDate as nextDate
|
|
65
|
+
if (startDate) {
|
|
66
|
+
startDate = moment
|
|
67
|
+
.tz(moment(startDate).format("YYYY-MM-DD HH:mm"), timezone!)
|
|
68
|
+
.toDate();
|
|
69
|
+
if (startDate > nextDate) {
|
|
70
|
+
cronOptions.currentDate = startDate;
|
|
71
|
+
cronTime = parser.parseExpression(interval!, cronOptions);
|
|
72
|
+
nextDate = cronTime.next().toDate();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If job has run in the past and skipDays is not null, add skipDays to nextDate
|
|
77
|
+
if (dateNow > lastRun && skipDays !== null) {
|
|
78
|
+
try {
|
|
79
|
+
nextDate = new Date(
|
|
80
|
+
nextDate.getTime() + (humanInterval(skipDays) ?? 0)
|
|
81
|
+
);
|
|
82
|
+
} catch {}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If endDate is less than the nextDate, set nextDate to null to stop the job from running further
|
|
86
|
+
if (endDate) {
|
|
87
|
+
const endDateDate: Date = moment
|
|
88
|
+
.tz(moment(endDate).format("YYYY-MM-DD HH:mm"), timezone!)
|
|
89
|
+
.toDate();
|
|
90
|
+
if (nextDate > endDateDate) {
|
|
91
|
+
nextDate = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.attrs.nextRunAt = nextDate;
|
|
96
|
+
debug(
|
|
97
|
+
"[%s:%s] nextRunAt set to [%s]",
|
|
98
|
+
this.attrs.name,
|
|
99
|
+
this.attrs._id,
|
|
100
|
+
this.attrs.nextRunAt?.toISOString()
|
|
101
|
+
);
|
|
102
|
+
// Either `xo` linter or Node.js 8 stumble on this line if it isn't just ignored
|
|
103
|
+
} catch {
|
|
104
|
+
debug(
|
|
105
|
+
"[%s:%s] failed nextRunAt based on interval [%s]",
|
|
106
|
+
this.attrs.name,
|
|
107
|
+
this.attrs._id,
|
|
108
|
+
interval
|
|
109
|
+
);
|
|
110
|
+
// Nope, humanInterval then!
|
|
111
|
+
try {
|
|
112
|
+
if (!this.attrs.lastRunAt && humanInterval(interval)) {
|
|
113
|
+
this.attrs.nextRunAt = lastRun;
|
|
114
|
+
debug(
|
|
115
|
+
"[%s:%s] nextRunAt set to [%s]",
|
|
116
|
+
this.attrs.name,
|
|
117
|
+
this.attrs._id,
|
|
118
|
+
this.attrs.nextRunAt.toISOString()
|
|
119
|
+
);
|
|
120
|
+
} else {
|
|
121
|
+
this.attrs.nextRunAt = new Date(
|
|
122
|
+
lastRun.getTime() + (humanInterval(interval) ?? 0)
|
|
123
|
+
);
|
|
124
|
+
debug(
|
|
125
|
+
"[%s:%s] nextRunAt set to [%s]",
|
|
126
|
+
this.attrs.name,
|
|
127
|
+
this.attrs._id,
|
|
128
|
+
this.attrs.nextRunAt.toISOString()
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
// Either `xo` linter or Node.js 8 stumble on this line if it isn't just ignored
|
|
132
|
+
} catch {}
|
|
133
|
+
} finally {
|
|
134
|
+
if (!this.attrs.nextRunAt?.getTime()) {
|
|
135
|
+
this.attrs.nextRunAt = undefined;
|
|
136
|
+
debug(
|
|
137
|
+
"[%s:%s] failed to calculate nextRunAt due to invalid repeat interval",
|
|
138
|
+
this.attrs.name,
|
|
139
|
+
this.attrs._id
|
|
140
|
+
);
|
|
141
|
+
this.fail(
|
|
142
|
+
"failed to calculate nextRunAt due to invalid repeat interval"
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Internal method to compute next run time from the repeat string
|
|
150
|
+
*/
|
|
151
|
+
const computeFromRepeatAt = () => {
|
|
152
|
+
const lastRun = this.attrs.lastRunAt || new Date();
|
|
153
|
+
const nextDate: Date = date(repeatAt);
|
|
154
|
+
|
|
155
|
+
// If you do not specify offset date for below test it will fail for ms
|
|
156
|
+
const offset = Date.now();
|
|
157
|
+
if (offset === date(repeatAt, offset).getTime()) {
|
|
158
|
+
this.attrs.nextRunAt = undefined;
|
|
159
|
+
debug(
|
|
160
|
+
"[%s:%s] failed to calculate repeatAt due to invalid format",
|
|
161
|
+
this.attrs.name,
|
|
162
|
+
this.attrs._id
|
|
163
|
+
);
|
|
164
|
+
this.fail("failed to calculate repeatAt time due to invalid format");
|
|
165
|
+
} else if (nextDate.getTime() === lastRun.getTime()) {
|
|
166
|
+
this.attrs.nextRunAt = date("tomorrow at ", repeatAt);
|
|
167
|
+
debug(
|
|
168
|
+
"[%s:%s] nextRunAt set to [%s]",
|
|
169
|
+
this.attrs.name,
|
|
170
|
+
this.attrs._id,
|
|
171
|
+
this.attrs.nextRunAt?.toISOString()
|
|
172
|
+
);
|
|
173
|
+
} else {
|
|
174
|
+
this.attrs.nextRunAt = date(repeatAt);
|
|
175
|
+
debug(
|
|
176
|
+
"[%s:%s] nextRunAt set to [%s]",
|
|
177
|
+
this.attrs.name,
|
|
178
|
+
this.attrs._id,
|
|
179
|
+
this.attrs.nextRunAt?.toISOString()
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
if (interval) {
|
|
185
|
+
computeFromInterval();
|
|
186
|
+
} else if (repeatAt) {
|
|
187
|
+
computeFromRepeatAt();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return this;
|
|
191
|
+
};
|
package/lib/job/fail.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import createDebugger from "debug";
|
|
2
|
+
import { Job } from ".";
|
|
3
|
+
|
|
4
|
+
const debug = createDebugger("agenda:job");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Fails the job with a reason (error) specified
|
|
8
|
+
* @name Job#fail
|
|
9
|
+
* @function
|
|
10
|
+
* @param reason reason job failed
|
|
11
|
+
*/
|
|
12
|
+
export const fail = function (this: Job, reason: string | Error): Job {
|
|
13
|
+
if (reason instanceof Error) {
|
|
14
|
+
reason = reason.message;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.attrs.failReason = reason;
|
|
18
|
+
this.attrs.failCount = (this.attrs.failCount || 0) + 1;
|
|
19
|
+
const now = new Date();
|
|
20
|
+
this.attrs.failedAt = now;
|
|
21
|
+
this.attrs.lastFinishedAt = now;
|
|
22
|
+
debug(
|
|
23
|
+
"[%s:%s] fail() called [%d] times so far",
|
|
24
|
+
this.attrs.name,
|
|
25
|
+
this.attrs._id,
|
|
26
|
+
this.attrs.failCount
|
|
27
|
+
);
|
|
28
|
+
return this;
|
|
29
|
+
};
|
package/lib/job/index.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { toJson } from "./to-json";
|
|
2
|
+
import { computeNextRunAt } from "./compute-next-run-at";
|
|
3
|
+
import { repeatEvery } from "./repeat-every";
|
|
4
|
+
import { repeatAt } from "./repeat-at";
|
|
5
|
+
import { disable } from "./disable";
|
|
6
|
+
import { enable } from "./enable";
|
|
7
|
+
import { unique } from "./unique";
|
|
8
|
+
import { schedule } from "./schedule";
|
|
9
|
+
import { priority } from "./priority";
|
|
10
|
+
import { fail } from "./fail";
|
|
11
|
+
import { run } from "./run";
|
|
12
|
+
import { isRunning } from "./is-running";
|
|
13
|
+
import { save } from "./save";
|
|
14
|
+
import { remove } from "./remove";
|
|
15
|
+
import { touch } from "./touch";
|
|
16
|
+
import { setShouldSaveResult } from "./set-shouldsaveresult";
|
|
17
|
+
import { parsePriority } from "../utils";
|
|
18
|
+
import { Agenda } from "../agenda";
|
|
19
|
+
import { JobPriority } from "../agenda/define";
|
|
20
|
+
import * as mongodb from "mongodb";
|
|
21
|
+
|
|
22
|
+
type Modify<T, R> = Omit<T, keyof R> & R
|
|
23
|
+
|
|
24
|
+
export interface JobAttributesData {
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
}
|
|
27
|
+
export interface JobAttributes<
|
|
28
|
+
T extends JobAttributesData = JobAttributesData
|
|
29
|
+
> {
|
|
30
|
+
/**
|
|
31
|
+
* The record identity.
|
|
32
|
+
*/
|
|
33
|
+
_id: mongodb.ObjectId;
|
|
34
|
+
|
|
35
|
+
agenda: Agenda;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The type of the job (single|normal).
|
|
39
|
+
*/
|
|
40
|
+
type: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The name of the job.
|
|
44
|
+
*/
|
|
45
|
+
name: string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Job's state
|
|
49
|
+
*/
|
|
50
|
+
disabled?: boolean;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Date/time the job will run next.
|
|
54
|
+
*/
|
|
55
|
+
nextRunAt?: Date | null;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Date/time the job was locked.
|
|
59
|
+
*/
|
|
60
|
+
lockedAt?: Date | null;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The priority of the job.
|
|
64
|
+
*/
|
|
65
|
+
priority: number | string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The job details.
|
|
69
|
+
*/
|
|
70
|
+
data: T;
|
|
71
|
+
|
|
72
|
+
unique?: any;
|
|
73
|
+
uniqueOpts?: {
|
|
74
|
+
insertOnly: boolean;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* How often the job is repeated using a human-readable or cron format.
|
|
79
|
+
*/
|
|
80
|
+
repeatInterval?: string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The timezone that conforms to [moment-timezone](http://momentjs.com/timezone/).
|
|
84
|
+
*/
|
|
85
|
+
repeatTimezone?: string | null;
|
|
86
|
+
|
|
87
|
+
repeatAt?: string;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Date/time the job was last run.
|
|
91
|
+
*/
|
|
92
|
+
lastRunAt?: Date;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Date/time the job last finished running.
|
|
96
|
+
*/
|
|
97
|
+
lastFinishedAt?: Date;
|
|
98
|
+
|
|
99
|
+
startDate?: Date | number | null;
|
|
100
|
+
endDate?: Date | number | null;
|
|
101
|
+
skipDays?: string | null;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The reason the job failed.
|
|
105
|
+
*/
|
|
106
|
+
failReason?: string;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The number of times the job has failed.
|
|
110
|
+
*/
|
|
111
|
+
failCount?: number;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The date/time the job last failed.
|
|
115
|
+
*/
|
|
116
|
+
failedAt?: Date;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Date/time the job was last modified.
|
|
120
|
+
*/
|
|
121
|
+
lastModifiedBy?: string;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Should the return value of the job be persisted.
|
|
125
|
+
*/
|
|
126
|
+
shouldSaveResult?: boolean;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Result of the finished job.
|
|
130
|
+
*/
|
|
131
|
+
result?: unknown;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @class
|
|
136
|
+
* @param {Object} args - Job Options
|
|
137
|
+
* @property {Object} agenda - The Agenda instance
|
|
138
|
+
* @property {Object} attrs
|
|
139
|
+
*/
|
|
140
|
+
class Job<T extends JobAttributesData = JobAttributesData> {
|
|
141
|
+
/**
|
|
142
|
+
* The agenda that created the job.
|
|
143
|
+
*/
|
|
144
|
+
agenda: Agenda;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The database record associated with the job.
|
|
148
|
+
*/
|
|
149
|
+
attrs: JobAttributes<T>;
|
|
150
|
+
|
|
151
|
+
toJSON!: typeof toJson;
|
|
152
|
+
computeNextRunAt!: typeof computeNextRunAt;
|
|
153
|
+
repeatEvery!: typeof repeatEvery;
|
|
154
|
+
repeatAt!: typeof repeatAt;
|
|
155
|
+
disable!: typeof disable;
|
|
156
|
+
enable!: typeof enable;
|
|
157
|
+
unique!: typeof unique;
|
|
158
|
+
schedule!: typeof schedule;
|
|
159
|
+
priority!: typeof priority;
|
|
160
|
+
fail!: typeof fail;
|
|
161
|
+
run!: typeof run;
|
|
162
|
+
isRunning!: typeof isRunning;
|
|
163
|
+
save!: typeof save;
|
|
164
|
+
remove!: typeof remove;
|
|
165
|
+
touch!: typeof touch;
|
|
166
|
+
setShouldSaveResult!: typeof setShouldSaveResult;
|
|
167
|
+
|
|
168
|
+
constructor(options: Modify<JobAttributes<T>, { _id?: mongodb.ObjectId; }>) {
|
|
169
|
+
const { agenda, type, nextRunAt, ...args } = options ?? {};
|
|
170
|
+
|
|
171
|
+
// Save Agenda instance
|
|
172
|
+
this.agenda = agenda;
|
|
173
|
+
|
|
174
|
+
// Set priority
|
|
175
|
+
args.priority =
|
|
176
|
+
args.priority === undefined
|
|
177
|
+
? JobPriority.normal
|
|
178
|
+
: parsePriority(args.priority);
|
|
179
|
+
|
|
180
|
+
// Set shouldSaveResult option
|
|
181
|
+
args.shouldSaveResult = args.shouldSaveResult || false
|
|
182
|
+
|
|
183
|
+
// Set attrs to args
|
|
184
|
+
const attrs: any = {};
|
|
185
|
+
for (const key in args) {
|
|
186
|
+
if ({}.hasOwnProperty.call(args, key)) {
|
|
187
|
+
// @ts-expect-error
|
|
188
|
+
attrs[key] = args[key];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Set defaults if undefined
|
|
193
|
+
this.attrs = {
|
|
194
|
+
...attrs,
|
|
195
|
+
// NOTE: What is the difference between 'once' here and 'single' in agenda/index.js?
|
|
196
|
+
name: attrs.name || "",
|
|
197
|
+
priority: attrs.priority,
|
|
198
|
+
type: type || "once",
|
|
199
|
+
nextRunAt: nextRunAt || new Date(),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
Job.prototype.toJSON = toJson;
|
|
205
|
+
Job.prototype.computeNextRunAt = computeNextRunAt;
|
|
206
|
+
Job.prototype.repeatEvery = repeatEvery;
|
|
207
|
+
Job.prototype.repeatAt = repeatAt;
|
|
208
|
+
Job.prototype.disable = disable;
|
|
209
|
+
Job.prototype.enable = enable;
|
|
210
|
+
Job.prototype.unique = unique;
|
|
211
|
+
Job.prototype.schedule = schedule;
|
|
212
|
+
Job.prototype.priority = priority;
|
|
213
|
+
Job.prototype.fail = fail;
|
|
214
|
+
Job.prototype.run = run;
|
|
215
|
+
Job.prototype.isRunning = isRunning;
|
|
216
|
+
Job.prototype.save = save;
|
|
217
|
+
Job.prototype.remove = remove;
|
|
218
|
+
Job.prototype.touch = touch;
|
|
219
|
+
Job.prototype.setShouldSaveResult = setShouldSaveResult;
|
|
220
|
+
|
|
221
|
+
export { Job };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Job } from ".";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A job is running if:
|
|
5
|
+
* (lastRunAt exists AND lastFinishedAt does not exist)
|
|
6
|
+
* OR
|
|
7
|
+
* (lastRunAt exists AND lastFinishedAt exists but the lastRunAt is newer [in time] than lastFinishedAt)
|
|
8
|
+
* @name Job#isRunning
|
|
9
|
+
* @function
|
|
10
|
+
* @returns Whether or not job is running at the moment (true for running)
|
|
11
|
+
*/
|
|
12
|
+
export const isRunning = function (this: Job): boolean {
|
|
13
|
+
if (!this.attrs.lastRunAt) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (!this.attrs.lastFinishedAt) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (
|
|
22
|
+
this.attrs.lockedAt &&
|
|
23
|
+
this.attrs.lastRunAt.getTime() > this.attrs.lastFinishedAt.getTime()
|
|
24
|
+
) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return false;
|
|
29
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Job } from ".";
|
|
2
|
+
import { parsePriority } from "../utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sets priority of the job
|
|
6
|
+
* @param priority priority of when job should be queued
|
|
7
|
+
*/
|
|
8
|
+
export const priority = function (this: Job, priority: string): Job {
|
|
9
|
+
this.attrs.priority = parsePriority(priority);
|
|
10
|
+
return this;
|
|
11
|
+
};
|