-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fea: add schedule module * fix: missing `this` scope to callback * enh: create an example of how to use it * fea: teardown scheduled listeners * fea: add test case * fix: update example * fea: add CronExpression enum * enh: isolate logic to register cron jobs * fea: add @interval * fea: add @timeout * enh: mock Deno.cron to unit test schedule module * enh: add tests for @timeout * enh: add tests for @interval * ref: reuse registration logic * ref: use SetMetadata utils * fix: add license to enum file
- Loading branch information
1 parent
8e7c930
commit cc251f0
Showing
10 changed files
with
409 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { | ||
Cron, | ||
DanetApplication, | ||
Interval, | ||
IntervalExpression, | ||
Module, | ||
ScheduleModule, | ||
Timeout, | ||
} from '../mod.ts'; | ||
|
||
class TaskScheduler { | ||
getNow() { | ||
return { | ||
now: new Date(), | ||
}; | ||
} | ||
|
||
@Timeout(IntervalExpression.SECOND) | ||
runOnce() { | ||
console.log('run once after 1s', this.getNow()); | ||
} | ||
|
||
@Interval(IntervalExpression.SECOND) | ||
runEachSecond() { | ||
console.log('1 sec', this.getNow()); | ||
} | ||
|
||
@Cron('*/1 * * * *') | ||
runEachMinute() { | ||
console.log('1 minute', this.getNow()); | ||
} | ||
|
||
@Cron('*/2 * * * *') | ||
runEach2Min() { | ||
console.log('2 minutes', this.getNow()); | ||
} | ||
|
||
@Cron('*/3 * * * *') | ||
runEach3Min() { | ||
console.log('3 minutes', this.getNow()); | ||
} | ||
} | ||
|
||
@Module({ | ||
imports: [ScheduleModule], | ||
injectables: [TaskScheduler], | ||
}) | ||
class AppModule {} | ||
|
||
const app = new DanetApplication(); | ||
await app.init(AppModule); | ||
|
||
let port = Number(Deno.env.get('PORT')); | ||
if (isNaN(port)) { | ||
port = 3000; | ||
} | ||
app.listen(port); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { | ||
Cron, | ||
CronExpression, | ||
DanetApplication, | ||
Interval, | ||
IntervalExpression, | ||
Module, | ||
ScheduleModule, | ||
Timeout, | ||
} from '../mod.ts'; | ||
import { assertEquals } from '../src/deps_test.ts'; | ||
import { assertSpyCallArg, FakeTime, spy } from '../src/deps_test.ts'; | ||
|
||
Deno.test('Schedule Module', async (t) => { | ||
const cron = Deno.cron; | ||
// @ts-ignore:next-line | ||
Deno.cron = spy(); | ||
|
||
class TestListener { | ||
@Cron(CronExpression.EVERY_MINUTE) | ||
runEachMinute() {} | ||
} | ||
|
||
@Module({ | ||
imports: [ScheduleModule], | ||
injectables: [TestListener], | ||
}) | ||
class TestModule {} | ||
|
||
const application = new DanetApplication(); | ||
await application.init(TestModule); | ||
await application.listen(0); | ||
|
||
await t.step('cronjob was called', () => { | ||
// @ts-ignore:next-line | ||
assertSpyCallArg(Deno.cron, 0, 0, 'runEachMinute'); | ||
// @ts-ignore:next-line | ||
assertSpyCallArg(Deno.cron, 0, 1, CronExpression.EVERY_MINUTE); | ||
}); | ||
|
||
await application.close(); | ||
Deno.cron = cron; | ||
}); | ||
|
||
Deno.test('Timeout Module', async (t) => { | ||
const time = new FakeTime(); | ||
const cb = spy(); | ||
|
||
class TestListener { | ||
@Timeout(IntervalExpression.MILISECOND) | ||
runEachMinute() { | ||
cb(); | ||
} | ||
} | ||
|
||
@Module({ | ||
imports: [ScheduleModule], | ||
injectables: [TestListener], | ||
}) | ||
class TestModule {} | ||
|
||
const application = new DanetApplication(); | ||
await application.init(TestModule); | ||
await application.listen(0); | ||
|
||
await t.step('should be called once after tick', async () => { | ||
await time.tickAsync(IntervalExpression.MILISECOND); | ||
assertEquals(cb.calls.length, 1); | ||
|
||
await time.tickAsync(IntervalExpression.MILISECOND); | ||
assertEquals(cb.calls.length, 1); | ||
}); | ||
|
||
await application.close(); | ||
}); | ||
|
||
Deno.test('Interval Module', async (t) => { | ||
const time = new FakeTime(); | ||
const cb = spy(); | ||
|
||
class TestListener { | ||
@Interval(IntervalExpression.MILISECOND) | ||
runEachMinute() { | ||
cb(); | ||
} | ||
} | ||
|
||
@Module({ | ||
imports: [ScheduleModule], | ||
injectables: [TestListener], | ||
}) | ||
class TestModule {} | ||
|
||
const application = new DanetApplication(); | ||
await application.init(TestModule); | ||
await application.listen(0); | ||
|
||
await t.step('should be called once after tick', async () => { | ||
await time.tickAsync(IntervalExpression.MILISECOND); | ||
assertEquals(cb.calls.length, 1); | ||
|
||
await time.tickAsync(IntervalExpression.MILISECOND); | ||
assertEquals(cb.calls.length, 2); | ||
|
||
await time.tickAsync(IntervalExpression.MILISECOND); | ||
assertEquals(cb.calls.length, 3); | ||
}); | ||
|
||
await application.close(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
export { | ||
assertSpyCall, | ||
assertSpyCallArg, | ||
assertSpyCalls, | ||
spy, | ||
} from 'https://deno.land/[email protected]/testing/mock.ts'; | ||
export { FakeTime } from 'https://deno.land/[email protected]/testing/time.ts'; | ||
export { | ||
assertEquals, | ||
assertInstanceOf, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const scheduleMetadataKey = 'task-scheduler'; | ||
export const intervalMetadataKey = 'interval'; | ||
export const timeoutMetadataKey = 'timeout'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { SetMetadata } from '../metadata/decorator.ts'; | ||
import { | ||
intervalMetadataKey, | ||
scheduleMetadataKey, | ||
timeoutMetadataKey, | ||
} from './constants.ts'; | ||
import { CronString } from './types.ts'; | ||
|
||
export const Cron = (cron: CronString): MethodDecorator => | ||
SetMetadata(scheduleMetadataKey, { cron }); | ||
|
||
export const Interval = (interval: number): MethodDecorator => | ||
SetMetadata(intervalMetadataKey, { interval }); | ||
|
||
export const Timeout = (timeout: number): MethodDecorator => | ||
SetMetadata(timeoutMetadataKey, { timeout }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// Copyright (c) 2017-2024 Kamil Mysliwiec MIT | ||
|
||
export enum CronExpression { | ||
EVERY_MINUTE = '*/1 * * * *', | ||
EVERY_5_MINUTES = '*/5 * * * *', | ||
EVERY_10_MINUTES = '*/10 * * * *', | ||
EVERY_30_MINUTES = '*/30 * * * *', | ||
EVERY_HOUR = '0 0-23/1 * * *', | ||
EVERY_2_HOURS = '0 0-23/2 * * *', | ||
EVERY_3_HOURS = '0 0-23/3 * * *', | ||
EVERY_4_HOURS = '0 0-23/4 * * *', | ||
EVERY_5_HOURS = '0 0-23/5 * * *', | ||
EVERY_6_HOURS = '0 0-23/6 * * *', | ||
EVERY_7_HOURS = '0 0-23/7 * * *', | ||
EVERY_8_HOURS = '0 0-23/8 * * *', | ||
EVERY_9_HOURS = '0 0-23/9 * * *', | ||
EVERY_10_HOURS = '0 0-23/10 * * *', | ||
EVERY_11_HOURS = '0 0-23/11 * * *', | ||
EVERY_12_HOURS = '0 0-23/12 * * *', | ||
EVERY_DAY_AT_1AM = '0 01 * * *', | ||
EVERY_DAY_AT_2AM = '0 02 * * *', | ||
EVERY_DAY_AT_3AM = '0 03 * * *', | ||
EVERY_DAY_AT_4AM = '0 04 * * *', | ||
EVERY_DAY_AT_5AM = '0 05 * * *', | ||
EVERY_DAY_AT_6AM = '0 06 * * *', | ||
EVERY_DAY_AT_7AM = '0 07 * * *', | ||
EVERY_DAY_AT_8AM = '0 08 * * *', | ||
EVERY_DAY_AT_9AM = '0 09 * * *', | ||
EVERY_DAY_AT_10AM = '0 10 * * *', | ||
EVERY_DAY_AT_11AM = '0 11 * * *', | ||
EVERY_DAY_AT_NOON = '0 12 * * *', | ||
EVERY_DAY_AT_1PM = '0 13 * * *', | ||
EVERY_DAY_AT_2PM = '0 14 * * *', | ||
EVERY_DAY_AT_3PM = '0 15 * * *', | ||
EVERY_DAY_AT_4PM = '0 16 * * *', | ||
EVERY_DAY_AT_5PM = '0 17 * * *', | ||
EVERY_DAY_AT_6PM = '0 18 * * *', | ||
EVERY_DAY_AT_7PM = '0 19 * * *', | ||
EVERY_DAY_AT_8PM = '0 20 * * *', | ||
EVERY_DAY_AT_9PM = '0 21 * * *', | ||
EVERY_DAY_AT_10PM = '0 22 * * *', | ||
EVERY_DAY_AT_11PM = '0 23 * * *', | ||
EVERY_DAY_AT_MIDNIGHT = '0 0 * * *', | ||
EVERY_WEEK = '0 0 * * 0', | ||
EVERY_WEEKDAY = '0 0 * * 1-5', | ||
EVERY_WEEKEND = '0 0 * * 6,0', | ||
EVERY_1ST_DAY_OF_MONTH_AT_MIDNIGHT = '0 0 1 * *', | ||
EVERY_1ST_DAY_OF_MONTH_AT_NOON = '0 12 1 * *', | ||
EVERY_2ND_HOUR = '0 */2 * * *', | ||
EVERY_2ND_HOUR_FROM_1AM_THROUGH_11PM = '0 1-23/2 * * *', | ||
EVERY_2ND_MONTH = '0 0 1 */2 *', | ||
EVERY_QUARTER = '0 0 1 */3 *', | ||
EVERY_6_MONTHS = '0 0 1 */6 *', | ||
EVERY_YEAR = '0 0 1 0 *', | ||
EVERY_30_MINUTES_BETWEEN_9AM_AND_5PM = '0 */30 9-17 * * *', | ||
EVERY_30_MINUTES_BETWEEN_9AM_AND_6PM = '0 */30 9-18 * * *', | ||
EVERY_30_MINUTES_BETWEEN_10AM_AND_7PM = '0 */30 10-19 * * *', | ||
MONDAY_TO_FRIDAY_AT_1AM = '0 0 01 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_2AM = '0 0 02 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_3AM = '0 0 03 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_4AM = '0 0 04 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_5AM = '0 0 05 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_6AM = '0 0 06 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_7AM = '0 0 07 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_8AM = '0 0 08 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_9AM = '0 0 09 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_09_30AM = '0 30 09 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_10AM = '0 0 10 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_11AM = '0 0 11 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_11_30AM = '0 30 11 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_12PM = '0 0 12 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_1PM = '0 0 13 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_2PM = '0 0 14 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_3PM = '0 0 15 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_4PM = '0 0 16 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_5PM = '0 0 17 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_6PM = '0 0 18 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_7PM = '0 0 19 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_8PM = '0 0 20 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_9PM = '0 0 21 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_10PM = '0 0 22 * * 1-5', | ||
MONDAY_TO_FRIDAY_AT_11PM = '0 0 23 * * 1-5', | ||
} | ||
|
||
export enum IntervalExpression { | ||
MILISECOND = 1, | ||
SECOND = 1000, | ||
MINUTE = 1000 * 60, | ||
HOUR = 1000 * 60 * 60, | ||
DAY = 1000 * 60 * 60 * 24, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './decorator.ts'; | ||
export * from './module.ts'; | ||
export * from './enum.ts'; |
Oops, something went wrong.