From 4186fb51f66043b221237c7ba91ac5a95a4f32b0 Mon Sep 17 00:00:00 2001 From: eug-vs Date: Wed, 2 Dec 2020 02:22:38 +0300 Subject: feat: add log model --- lib/event.model.ts | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/event.schema.ts | 34 ++++++++++++++++++ lib/log.model.ts | 8 +++++ lib/log.schema.ts | 15 ++++++++ lib/model.ts | 92 ----------------------------------------------- lib/scheduler.ts | 2 +- lib/schema.ts | 34 ------------------ 7 files changed, 158 insertions(+), 127 deletions(-) create mode 100644 lib/event.model.ts create mode 100644 lib/event.schema.ts create mode 100644 lib/log.model.ts create mode 100644 lib/log.schema.ts delete mode 100644 lib/model.ts delete mode 100644 lib/schema.ts diff --git a/lib/event.model.ts b/lib/event.model.ts new file mode 100644 index 0000000..a430674 --- /dev/null +++ b/lib/event.model.ts @@ -0,0 +1,100 @@ +import { model, Schema, Model } from 'mongoose'; +import cron from 'cron'; +import createEventSchema, { EventDocument } from './event.schema'; +import { LogDocument } from './log.schema'; +import LogModel from './log.model'; + +interface Event extends EventDocument { + log(message: string): void; + start(): void; + complete(): void; + fail(error: Error): void; + computeNextRunAt(): Date; + getLogs(): Promise; +} + +export interface EventModel extends Model> { + findNextEvents(): Event[]; + findMissedEvents(): Event[]; +} + +const CronJob = cron.CronJob; + +const createEventModel = (name: string, contextSchema: Schema): EventModel => { + const schema = createEventSchema(contextSchema); + + // Schema methods + schema.method('log', function(message: string) { + const timestamp = new Date().toLocaleString('en'); + console.log(`[${timestamp}] ${this.name}: ${message}`); + return LogModel.create({ eventId: this._id, 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(); + }); + + schema.method('getLogs', function() { + return LogModel.find({ eventId: this._id }); + }); + + // 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>('save', async function() { + this.nextRunAt = this.computeNextRunAt(); + }); + + return model, EventModel>(name, schema); +}; + + +export default createEventModel; + diff --git a/lib/event.schema.ts b/lib/event.schema.ts new file mode 100644 index 0000000..1bba77d --- /dev/null +++ b/lib/event.schema.ts @@ -0,0 +1,34 @@ +import { Schema, Document } from 'mongoose'; + +export interface EventDocument 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 + }, + schedule: { + type: String, + required: true + }, + status: { + type: String, + default: 'notStarted' + }, + error: String, + context: contextSchema, + nextRunAt: Date, + lastRunAt: Date +}, { timestamps: true }); + + +export default createEventSchema; + diff --git a/lib/log.model.ts b/lib/log.model.ts new file mode 100644 index 0000000..7ea9cb9 --- /dev/null +++ b/lib/log.model.ts @@ -0,0 +1,8 @@ +import { model, Model } from 'mongoose'; +import schema, { LogDocument } from './log.schema'; + +const LogModel = model>('Log', schema); + + +export default LogModel; + diff --git a/lib/log.schema.ts b/lib/log.schema.ts new file mode 100644 index 0000000..3c55de7 --- /dev/null +++ b/lib/log.schema.ts @@ -0,0 +1,15 @@ +import { Schema, Types, Document } from 'mongoose'; + +export interface LogDocument extends Document { + eventId: string; + message: string; +} + +const schema = new Schema({ + eventId: Types.ObjectId, + message: String +}, { timestamps: true }); + + +export default schema; + diff --git a/lib/model.ts b/lib/model.ts deleted file mode 100644 index f93928e..0000000 --- a/lib/model.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { model, Schema, Model } from 'mongoose'; -import createEventSchema, { EventDocument } from './schema'; -import cron from 'cron'; - -interface Event extends EventDocument { - log(message: string): void; - start(): void; - complete(): void; - fail(error: Error): void; - computeNextRunAt(): Date; -} - -export interface EventModel extends Model> { - findNextEvents(): Event[]; - findMissedEvents(): Event[]; -} - -const CronJob = cron.CronJob; - -const createEventModel = (name: string, contextSchema: Schema): EventModel => { - 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>('save', async function() { - this.nextRunAt = this.computeNextRunAt(); - }); - - return model, EventModel>(name, schema); -}; - - -export default createEventModel; - diff --git a/lib/scheduler.ts b/lib/scheduler.ts index 449ac39..28d09cc 100644 --- a/lib/scheduler.ts +++ b/lib/scheduler.ts @@ -1,6 +1,6 @@ import cron from 'cron'; import Bluebird from 'bluebird'; -import { EventModel } from './model'; +import { EventModel } from './event.model'; const CronJob = cron.CronJob; diff --git a/lib/schema.ts b/lib/schema.ts deleted file mode 100644 index 1bba77d..0000000 --- a/lib/schema.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Schema, Document } from 'mongoose'; - -export interface EventDocument 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 - }, - schedule: { - type: String, - required: true - }, - status: { - type: String, - default: 'notStarted' - }, - error: String, - context: contextSchema, - nextRunAt: Date, - lastRunAt: Date -}, { timestamps: true }); - - -export default createEventSchema; - -- cgit v1.2.3