Skip to content

Commit 5016782

Browse files
committed
Merge branch 'fix/crontab-enh' into chore/all-my-stuffs
# Conflicts: # package.json # pnpm-lock.yaml
2 parents 4222451 + fa01008 commit 5016782

File tree

7 files changed

+285
-10
lines changed

7 files changed

+285
-10
lines changed

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@
117117
"convert": "^5.4.1",
118118
"country-code-lookup": "^0.1.0",
119119
"crc": "^4.3.2",
120+
"countries-and-timezones": "^3.6.0",
121+
"country-code-lookup": "^0.1.0",
122+
"cron-parser": "^4.9.0",
120123
"cron-validator": "^1.3.1",
121124
"cronstrue": "^2.26.0",
122125
"crypto-js": "^4.1.1",
@@ -138,6 +141,7 @@
138141
"emojilib": "^3.0.10",
139142
"fast_array_intersect": "^1.1.0",
140143
"exif-be-gone": "^1.5.1",
144+
"event-cron-parser": "^1.0.34",
141145
"figlet": "^1.7.0",
142146
"figue": "^1.2.0",
143147
"flatten-anything": "^4.0.1",

pnpm-lock.yaml

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { getCronType, getLastExecutionTimes, isCronValid } from './crontab-generator.service';
3+
4+
describe('crontab-generator', () => {
5+
describe('isCronValid', () => {
6+
it('should return true for all valid formats', () => {
7+
// standard format
8+
expect(isCronValid('0 0 * * 1-5')).toBe(true);
9+
expect(isCronValid('23 0-20/2 * * *')).toBe(true);
10+
11+
// AWS formats
12+
expect(isCronValid('0 11-22 ? * MON-FRI *')).toBe(true);
13+
expect(isCronValid('0 0 ? * 1 *')).toBe(true);
14+
});
15+
it('should check standard format', () => {
16+
// standard format
17+
expect(isCronValid('0 0 * * 1-5', 'standard')).toBe(true);
18+
expect(isCronValid('23 0-20/2 * * *', 'standard')).toBe(true);
19+
20+
// AWS format
21+
expect(isCronValid('0 11-22 ? * MON-FRI *', 'standard')).toBe(false);
22+
expect(isCronValid('0 0 ? * 1 *', 'standard')).toBe(false);
23+
});
24+
it('should check aws format', () => {
25+
// standard format
26+
expect(isCronValid('0 0 * * 1-5', 'aws')).toBe(false);
27+
expect(isCronValid('23 0-20/2 * * *', 'aws')).toBe(false);
28+
29+
// AWS format
30+
expect(isCronValid('0 11-22 ? * MON-FRI *', 'aws')).toBe(true);
31+
expect(isCronValid('0 0 ? * 1 *', 'aws')).toBe(true);
32+
});
33+
it('should return false for all invalid formats', () => {
34+
expect(isCronValid('aert')).toBe(false);
35+
expect(isCronValid('40 *')).toBe(false);
36+
});
37+
});
38+
39+
describe('getCronType', () => {
40+
it('should return right type', () => {
41+
expect(getCronType('0 0 * * 1-5')).toBe('standard');
42+
expect(getCronType('23 0-20/2 * * *')).toBe('standard');
43+
44+
// AWS formats
45+
expect(getCronType('0 11-22 ? * MON-FRI *')).toBe('aws');
46+
expect(getCronType('0 0 ? * 1 *')).toBe('aws');
47+
48+
expect(getCronType('aert')).toBe(false);
49+
expect(getCronType('40 *')).toBe(false);
50+
});
51+
});
52+
53+
describe('getLastExecutionTimes', () => {
54+
it('should return next valid datetimes', () => {
55+
expect(getLastExecutionTimes('0 0 * * 1-5')).toHaveLength(5);
56+
expect(getLastExecutionTimes('23 0-20/2 * * *')).toHaveLength(5);
57+
58+
// AWS formats
59+
expect(getLastExecutionTimes('0 11-22 ? * MON-FRI *')).toHaveLength(5);
60+
expect(getLastExecutionTimes('0 0 ? * 1 *')).toHaveLength(5);
61+
});
62+
});
63+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { parseExpression } from 'cron-parser';
2+
import EventCronParser from 'event-cron-parser';
3+
4+
export type CronType = 'standard' | 'aws';
5+
6+
export function getLastExecutionTimes(cronExpression: string, tz: string | undefined = undefined, count: number = 5) {
7+
if (getCronType(cronExpression) === 'standard') {
8+
const interval = parseExpression(cronExpression, { tz });
9+
const times = [];
10+
for (let i = 0; i < count; i++) {
11+
times.push(interval.next().toJSON());
12+
}
13+
return times;
14+
}
15+
if (getCronType(cronExpression) === 'aws') {
16+
const parsed = new EventCronParser(cronExpression);
17+
const times = [];
18+
for (let i = 0; i < count; i++) {
19+
times.push(JSON.stringify(parsed.next()));
20+
}
21+
return times;
22+
}
23+
24+
return [];
25+
}
26+
27+
export function isCronValid(cronExpression: string, cronType: CronType | 'any' = 'any') {
28+
const expressionCronType = getCronType(cronExpression);
29+
return cronType === 'any' ? !!expressionCronType : expressionCronType === cronType;
30+
}
31+
32+
export function getCronType(cronExpression: string) {
33+
try {
34+
parseExpression(cronExpression);
35+
return 'standard';
36+
}
37+
catch (_) {
38+
try {
39+
const parsed = new EventCronParser(cronExpression);
40+
parsed.validate();
41+
return 'aws';
42+
}
43+
catch (_) {
44+
}
45+
}
46+
return false;
47+
}

0 commit comments

Comments
 (0)