diff options
author | eug-vs <eug-vs@keemail.me> | 2020-12-02 00:29:25 +0300 |
---|---|---|
committer | eug-vs <eug-vs@keemail.me> | 2020-12-02 00:29:25 +0300 |
commit | 972e0578f999aadb5924d84768e59445c92f167b (patch) | |
tree | 7ec9a8c09f33b8a6f2aa0b3db5fb93889f410a29 | |
parent | fc3701733fca3fccd8330aaa5095fdcd82daf2b7 (diff) | |
download | mongo-cronjob-972e0578f999aadb5924d84768e59445c92f167b.tar.gz |
feat: add schema and model
-rw-r--r-- | lib/model.ts | 87 | ||||
-rw-r--r-- | lib/schema.ts | 35 |
2 files changed, 122 insertions, 0 deletions
diff --git a/lib/model.ts b/lib/model.ts new file mode 100644 index 0000000..77fede0 --- /dev/null +++ b/lib/model.ts @@ -0,0 +1,87 @@ +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(): void; + computeNextRunAt(): Date; +} + +const CronJob = cron.CronJob; + +const createEventModel = <Context extends Schema>(name: string, contextSchema: 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>>(name, schema); +}; + + +export default createEventModel; + diff --git a/lib/schema.ts b/lib/schema.ts new file mode 100644 index 0000000..4e5dd69 --- /dev/null +++ b/lib/schema.ts @@ -0,0 +1,35 @@ +import { Schema, Document } from 'mongoose'; + +export interface EventDocument<Context> extends Document { + name: string; + schedule: string; + status: 'notStarted' | 'running' | 'complete' | 'failed'; + error?: string; + context: Context; + nextRunAt: Date; + lastRunAt: Date; +} + +const createEventSchema = (contextSchema: Schema) => new Schema({ + name: { + type: String, + required: true, + unique: true + }, + schedule: { + type: String, + required: true + }, + status: { + type: String, + default: 'notStarted' + }, + error: String, + context: contextSchema, + nextRunAt: Date, + lastRunAt: Date +}, { timestamps: true }); + + +export default createEventSchema; + |