1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
import { model, Schema, Model } from 'mongoose';
import createEventSchema, { EventDocument } from './schema';
import cron from 'cron';
interface Event<Context> extends EventDocument<Context> {
log(message: string): void;
start(): void;
complete(): void;
fail(error: Error): void;
computeNextRunAt(): Date;
}
export interface EventModel<Context> extends Model<Event<Context>> {
findNextEvents(): Event<Context>[];
findMissedEvents(): Event<Context>[];
}
const CronJob = cron.CronJob;
const createEventModel = <Context extends Schema>(name: string, contextSchema: Context): EventModel<Context> => {
const schema = createEventSchema(contextSchema);
// Schema methods
schema.method('log', function(message: string) {
// TODO: Actually create logs
console.log(message);
});
schema.method('start', function() {
this.log('Event started')
this.lastRunAt = new Date();
this.status = 'running';
return this.save();
});
schema.method('complete', function() {
this.log('Event complete')
this.status = 'complete';
return this.save();
});
schema.method('fail', function(error: Error) {
this.log(error);
this.error = error;
this.status = 'failed';
return this.save();
});
schema.method('computeNextRunAt', function() {
const job = new CronJob(this.schedule);
const nextRunAt = job.nextDates();
return nextRunAt.toDate();
});
// Statics
schema.static('findMissedEvents', async function () {
return this.find({
nextRunAt: {
// TODO: skip single-fire events
$lt: new Date()
},
});
});
schema.static('findNextEvents', function(limit = 10) {
return this.find(
{
nextRunAt: {
$gt: new Date()
},
},
null,
{
sort: {
nextRunAt: 1
},
limit
}
)
});
// Hooks
schema.pre<Event<Context>>('save', async function() {
this.nextRunAt = this.computeNextRunAt();
});
return model<Event<Context>, EventModel<Context>>(name, schema);
};
export default createEventModel;
|