Skip to content

Commit 705e5c2

Browse files
committed
All crons and regex...
(...make Jack a dull boy) Signed-off-by: Alexander Trauzzi <[email protected]>
1 parent e2d9ac3 commit 705e5c2

File tree

5 files changed

+169
-47
lines changed

5 files changed

+169
-47
lines changed

src/implementation/Client/HTTPClient/jobs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import IClientJobs from "../../../interfaces/Client/IClientJobs";
1515
import HTTPClient from "./HTTPClient";
1616
import { THTTPExecuteParams } from "../../../types/http/THTTPExecuteParams.type";
1717
import { Job } from "../../../types/jobs/Job.type";
18-
import { Schedule, ScheduleUnboxer } from "../../../types/jobs/JobSchedule.type";
18+
import { Schedule, BoxedSchedule } from "../../../types/jobs/JobSchedule.type";
1919

2020
export default class HTTPClientJobs implements IClientJobs {
2121

@@ -33,7 +33,7 @@ export default class HTTPClientJobs implements IClientJobs {
3333
ttl: string | null = null,
3434
): Promise<void> {
3535

36-
const boxedSchedule = new ScheduleUnboxer(schedule);
36+
const boxedSchedule = new BoxedSchedule(schedule);
3737

3838
await this.httpClient.executeWithApiVersion(HTTPClientJobs.ApiVersion, `/${HTTPClientJobs.Path}/${jobName}`, {
3939
method: "POST",

src/types/jobs/CronExpression.type.ts

Lines changed: 118 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ export enum CronPeriod {
2323
Month,
2424
}
2525

26-
export type SecondOrMinuteValue = 0 | 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;
27-
export type HourValue = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23;
26+
export type SecondOrMinute = 0 | 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;
27+
export type Hour = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23;
2828
export type DayOfMonth = 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;
29+
export type DayOfWeekNumber = 0 | 1 | 2 | 3 | 4 | 5 | 6;
30+
export type MonthNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
2931

3032
export enum DayOfWeek {
3133
Sunday = "SUN",
@@ -37,6 +39,8 @@ export enum DayOfWeek {
3739
Saturday = "SAT",
3840
}
3941

42+
export type DayOfWeekValue = DayOfWeek | DayOfWeekNumber;
43+
4044
export enum Month {
4145
January = "JAN",
4246
February = "FEB",
@@ -52,35 +56,53 @@ export enum Month {
5256
December = "DEC",
5357
}
5458

59+
export type MonthValue = Month | MonthNumber;
60+
5561
export class CronExpressionBuilder {
5662
public static on(
5763
period: CronPeriod.Second,
58-
...values: (SecondOrMinuteValue[] | SecondOrMinuteValue)[]
64+
...values: (SecondOrMinute[] | SecondOrMinute)[]
5965
): CronExpression;
6066
public static on(
6167
period: CronPeriod.Minute,
62-
...values: (SecondOrMinuteValue[] | SecondOrMinuteValue)[]
68+
...values: (SecondOrMinute[] | SecondOrMinute)[]
6369
): CronExpression;
64-
public static on(period: CronPeriod.Hour, ...values: (HourValue[] | HourValue)[]): CronExpression;
65-
public static on(period: CronPeriod.DayOfWeek, ...values: (DayOfWeek[] | DayOfWeek)[]): CronExpression;
70+
public static on(period: CronPeriod.Hour, ...values: (Hour[] | Hour)[]): CronExpression;
71+
public static on(period: CronPeriod.DayOfWeek, ...values: (DayOfWeekValue[] | DayOfWeekValue)[]): CronExpression;
6672
public static on(period: CronPeriod.DayOfMonth, ...values: (DayOfMonth[] | DayOfMonth)[]): CronExpression;
67-
public static on(period: CronPeriod.Month, ...values: (Month[] | Month)[]): CronExpression;
73+
public static on(period: CronPeriod.Month, ...values: (MonthValue[] | MonthValue)[]): CronExpression;
6874
public static on(period: any, ...values: (any[] | any)[]) {
6975
return new CronExpression().on(period, values.flat());
7076
}
7177

72-
public static through(period: CronPeriod.Second, from: SecondOrMinuteValue, to: SecondOrMinuteValue): CronExpression;
73-
public static through(period: CronPeriod.Minute, from: SecondOrMinuteValue, to: SecondOrMinuteValue): CronExpression;
74-
public static through(period: CronPeriod.Hour, from: HourValue, to: HourValue): CronExpression;
75-
public static through(period: CronPeriod.DayOfWeek, from: DayOfWeek, to: DayOfWeek): CronExpression;
78+
public static through(period: CronPeriod.Second, from: SecondOrMinute, to: SecondOrMinute): CronExpression;
79+
public static through(period: CronPeriod.Minute, from: SecondOrMinute, to: SecondOrMinute): CronExpression;
80+
public static through(period: CronPeriod.Hour, from: Hour, to: Hour): CronExpression;
81+
public static through(period: CronPeriod.DayOfWeek, from: DayOfWeekValue, to: DayOfWeekValue): CronExpression;
7682
public static through(period: CronPeriod.DayOfMonth, from: DayOfMonth, to: DayOfMonth): CronExpression;
77-
public static through(period: CronPeriod.Month, from: Month, to: Month): CronExpression;
78-
public static through(period: any, from: any, to: any) {
83+
public static through(period: CronPeriod.Month, from: MonthValue, to: MonthValue): CronExpression;
84+
public static through(period: any, from: any, to: any): CronExpression {
7985
return new CronExpression().through(period, from, to);
8086
}
8187

82-
public static every(period: CronPeriod) {
83-
return new CronExpression().every(period);
88+
public static each(period: CronPeriod.Second): CronExpression;
89+
public static each(period: CronPeriod.Minute): CronExpression;
90+
public static each(period: CronPeriod.Hour): CronExpression;
91+
public static each(period: CronPeriod.DayOfWeek): CronExpression;
92+
public static each(period: CronPeriod.DayOfMonth): CronExpression;
93+
public static each(period: CronPeriod.Month): CronExpression;
94+
public static each(period: any) {
95+
return new CronExpression().each(period);
96+
}
97+
98+
public static every(period: CronPeriod.Second, interval: SecondOrMinute): CronExpression;
99+
public static every(period: CronPeriod.Minute, interval: SecondOrMinute): CronExpression;
100+
public static every(period: CronPeriod.Hour, interval: Hour): CronExpression;
101+
public static every(period: CronPeriod.DayOfWeek, interval: DayOfWeekValue): CronExpression;
102+
public static every(period: CronPeriod.DayOfMonth, interval: DayOfMonth): CronExpression;
103+
public static every(period: CronPeriod.Month, interval: MonthValue): CronExpression;
104+
public static every(period: any, interval: any) {
105+
return new CronExpression().every(period, interval);
84106
}
85107
}
86108

@@ -106,12 +128,12 @@ export class CronExpression {
106128
private month = "*";
107129
private dayOfWeek = "*";
108130

109-
public on(period: CronPeriod.Second, ...values: (SecondOrMinuteValue[] | SecondOrMinuteValue)[]): this;
110-
public on(period: CronPeriod.Minute, ...values: (SecondOrMinuteValue[] | SecondOrMinuteValue)[]): this;
111-
public on(period: CronPeriod.Hour, ...values: (HourValue[] | HourValue)[]): this;
112-
public on(period: CronPeriod.DayOfWeek, ...values: (DayOfWeek[] | DayOfWeek)[]): this;
131+
public on(period: CronPeriod.Second, ...values: (SecondOrMinute[] | SecondOrMinute)[]): this;
132+
public on(period: CronPeriod.Minute, ...values: (SecondOrMinute[] | SecondOrMinute)[]): this;
133+
public on(period: CronPeriod.Hour, ...values: (Hour[] | Hour)[]): this;
134+
public on(period: CronPeriod.DayOfWeek, ...values: (DayOfWeekValue[] | DayOfWeekValue)[]): this;
113135
public on(period: CronPeriod.DayOfMonth, ...values: (DayOfMonth[] | DayOfMonth)[]): this;
114-
public on(period: CronPeriod.Month, ...values: (Month[] | Month)[]): this;
136+
public on(period: CronPeriod.Month, ...values: (MonthValue[] | MonthValue)[]): this;
115137
public on(period: any, ...values: (any[] | any)[]): this {
116138
const fixedValues = values.flat();
117139

@@ -139,13 +161,13 @@ export class CronExpression {
139161
return this;
140162
}
141163

142-
public through(period: CronPeriod.Second, from: SecondOrMinuteValue, to: SecondOrMinuteValue): CronExpression;
143-
public through(period: CronPeriod.Minute, from: SecondOrMinuteValue, to: SecondOrMinuteValue): CronExpression;
144-
public through(period: CronPeriod.Hour, from: SecondOrMinuteValue, to: SecondOrMinuteValue): CronExpression;
145-
public through(period: CronPeriod.DayOfWeek, from: DayOfWeek, to: DayOfWeek): CronExpression;
164+
public through(period: CronPeriod.Second, from: SecondOrMinute, to: SecondOrMinute): CronExpression;
165+
public through(period: CronPeriod.Minute, from: SecondOrMinute, to: SecondOrMinute): CronExpression;
166+
public through(period: CronPeriod.Hour, from: SecondOrMinute, to: SecondOrMinute): CronExpression;
167+
public through(period: CronPeriod.DayOfWeek, from: DayOfWeekValue, to: DayOfWeekValue): CronExpression;
146168
public through(period: CronPeriod.DayOfMonth, from: DayOfMonth, to: DayOfMonth): CronExpression;
147-
public through(period: CronPeriod.Month, from: Month, to: Month): CronExpression;
148-
public through(period: any, from: any, to: any) {
169+
public through(period: CronPeriod.Month, from: MonthValue, to: MonthValue): CronExpression;
170+
public through(period: any, from: any, to: any): this {
149171
switch (period) {
150172
case CronPeriod.Second:
151173
this.seconds = this.prepareSecondOrMinuteValueRange(from, to);
@@ -170,7 +192,13 @@ export class CronExpression {
170192
return this;
171193
}
172194

173-
public every(period: CronPeriod): this {
195+
public each(period: CronPeriod.Second): this;
196+
public each(period: CronPeriod.Minute): this;
197+
public each(period: CronPeriod.Hour): this;
198+
public each(period: CronPeriod.DayOfWeek): this;
199+
public each(period: CronPeriod.DayOfMonth): this;
200+
public each(period: CronPeriod.Month): this;
201+
public each(period: any): this {
174202
switch (period) {
175203
case CronPeriod.Second:
176204
this.seconds = "*";
@@ -194,11 +222,44 @@ export class CronExpression {
194222
return this;
195223
}
196224

225+
public every(period: CronPeriod.Second, interval: SecondOrMinute): this;
226+
public every(period: CronPeriod.Minute, interval: SecondOrMinute): this;
227+
public every(period: CronPeriod.Hour, interval: Hour): this;
228+
public every(period: CronPeriod.DayOfWeek, interval: DayOfWeekValue): this;
229+
public every(period: CronPeriod.DayOfMonth, interval: DayOfMonth): this;
230+
public every(period: CronPeriod.Month, interval: MonthValue): this;
231+
public every(period: any, interval: any): this {
232+
233+
const intervalValue = `*/${interval}`;
234+
235+
switch (period) {
236+
case CronPeriod.Second:
237+
this.seconds = intervalValue;
238+
break;
239+
case CronPeriod.Minute:
240+
this.minutes = intervalValue;
241+
break;
242+
case CronPeriod.Hour:
243+
this.hours = intervalValue;
244+
break;
245+
case CronPeriod.DayOfWeek:
246+
this.dayOfWeek = intervalValue;
247+
break;
248+
case CronPeriod.DayOfMonth:
249+
this.dayOfMonth = intervalValue;
250+
break;
251+
case CronPeriod.Month:
252+
this.month = intervalValue;
253+
break;
254+
}
255+
return this;
256+
}
257+
197258
public toString(): CronExpressionString {
198259
return `${this.seconds} ${this.minutes} ${this.hours} ${this.dayOfMonth} ${this.month} ${this.dayOfWeek}`;
199260
}
200261

201-
private prepareSecondOrMinuteValueRange(from: SecondOrMinuteValue, to: SecondOrMinuteValue) {
262+
private prepareSecondOrMinuteValueRange(from: SecondOrMinute, to: SecondOrMinute) {
202263
if (from >= to) throw new Error("Invalid range: 'from' must be less than 'to'");
203264

204265
if ([from, to].some((v) => v < 0 || v > 59))
@@ -207,7 +268,7 @@ export class CronExpression {
207268
return `${from}-${to}`;
208269
}
209270

210-
private prepareHourValueRange(from: HourValue, to: HourValue) {
271+
private prepareHourValueRange(from: Hour, to: Hour) {
211272
if (from >= to) throw new Error("Invalid range: 'from' must be less than 'to'");
212273

213274
if ([from, to].some((v) => v < 0 || v > 23))
@@ -225,49 +286,69 @@ export class CronExpression {
225286
return `${from}-${to}`;
226287
}
227288

228-
private prepareMonthValueRange(from: Month, to: Month) {
229-
if (Object.values(Month).indexOf(from) >= Object.values(Month).indexOf(to))
289+
private prepareMonthValueRange(from: MonthValue, to: MonthValue) {
290+
if (typeof(from) !== "number" && typeof(to) !== "number" && Object.values(Month).indexOf(from) >= Object.values(Month).indexOf(to))
230291
throw new Error("Invalid range: 'from' must be less than 'to'");
231292

232293
return `${from}-${to}`;
233294
}
234295

235-
private prepareDayOfWeekValueRange(from: DayOfWeek, to: DayOfWeek) {
236-
if (Object.values(DayOfWeek).indexOf(from) >= Object.values(DayOfWeek).indexOf(to))
296+
private prepareDayOfWeekValueRange(from: DayOfWeekValue, to: DayOfWeekValue) {
297+
if (typeof(from) !== "number" && typeof(to) !== "number" && Object.values(DayOfWeek).indexOf(from) >= Object.values(DayOfWeek).indexOf(to))
237298
throw new Error("Invalid range: 'from' must be less than 'to'");
238299

239300
return `${from}-${to}`;
240301
}
241302

242-
private prepareSecondOrMinuteValueList(timeValues: SecondOrMinuteValue[]) {
303+
private prepareSecondOrMinuteValueList(timeValues: SecondOrMinute[]) {
304+
305+
if (! timeValues.length)
306+
throw new Error("Empty time value provided.");
307+
243308
if (timeValues.some((v) => v < 0 || v > 59))
244309
throw new RangeError("Time values must be within 0 and 59, inclusive.");
245310

246311
return [...timeValues].sort((a, b) => a - b).join(",");
247312
}
248313

249-
private prepareHourValueList(timeValues: HourValue[]) {
314+
private prepareHourValueList(timeValues: Hour[]) {
315+
316+
if (! timeValues.length)
317+
throw new Error("Empty hour value provided.");
318+
250319
if (timeValues.some((v) => v < 0 || v > 23))
251320
throw new RangeError("Hour values must be within 0 and 23, inclusive.");
252321

253322
return [...timeValues].sort((a, b) => a - b).join(",");
254323
}
255324

256325
private prepareDayOfMonthValueList(dayOfMonthValues: DayOfMonth[]) {
326+
327+
if (! dayOfMonthValues.length || dayOfMonthValues.some((v) => ! v))
328+
throw new Error("Empty day of month value provided.");
329+
257330
if (dayOfMonthValues.some((v) => v < 1 || v > 31))
258331
throw new RangeError("Day of month values must be within 1 and 31, inclusive.");
259332

260333
return [...dayOfMonthValues].sort((a, b) => a - b).join(",");
261334
}
262335

263336
private prepareMonthValueList(monthValues: Month[]) {
337+
338+
if (! monthValues.length || monthValues.some((v) => ! v))
339+
throw new Error("Empty month value provided.");
340+
264341
return [...monthValues]
265342
.sort((left, right) => Object.values(Month).indexOf(left) - Object.values(Month).indexOf(right))
266343
.join(",");
267344
}
268345

269-
private prepareDayOfWeekValueList(dayValues: DayOfWeek[]) {
270-
return [...dayValues]
346+
private prepareDayOfWeekValueList(dayOfWeekValues: DayOfWeek[]) {
347+
348+
if (! dayOfWeekValues.length || dayOfWeekValues.some((v) => ! v))
349+
throw new Error("Empty month value provided.");
350+
351+
return [...dayOfWeekValues]
271352
.sort((left, right) => Object.values(DayOfWeek).indexOf(left) - Object.values(DayOfWeek).indexOf(right))
272353
.join(",");
273354
}

src/types/jobs/JobSchedule.type.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ limitations under the License.
1313

1414
import { CronExpression, CronExpressionString } from "./CronExpression.type";
1515

16-
export enum PeriodConstant {
16+
export enum Period {
1717
Yearly = "@yearly",
1818
Monthly = "@monthly",
1919
Weekly = "@weekly",
@@ -46,9 +46,9 @@ class EveryExpression {
4646
}
4747
}
4848

49-
export type Schedule = PeriodConstant | EveryExpression | EveryExpressionString | CronExpression | CronExpressionString;
49+
export type Schedule = Period | EveryExpression | EveryExpressionString | CronExpression | CronExpressionString;
5050

51-
export class ScheduleUnboxer {
51+
export class BoxedSchedule {
5252

5353
private readonly value: Schedule | null;
5454

test/unit/jobs/cron.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,9 @@ describe("CRON Expressions", () => {
219219
const cronExpression = CronExpressionBuilder
220220
.on(CronPeriod.Month, [Month.December, Month.January])
221221
.on(CronPeriod.DayOfWeek, DayOfWeek.Wednesday)
222-
.every(CronPeriod.DayOfWeek)
222+
.every(CronPeriod.DayOfWeek, DayOfWeek.Sunday)
223223
.toString();
224224

225-
expect(cronExpression).toEqual("* * * * JAN,DEC *");
225+
expect(cronExpression).toEqual("* * * * JAN,DEC */SUN");
226226
});
227227
});

test/unit/jobs/jobs.test.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ limitations under the License.
1313

1414
import HTTPClient from "../../../src/implementation/Client/HTTPClient/HTTPClient";
1515
import HTTPClientJobs from "../../../src/implementation/Client/HTTPClient/jobs";
16-
import { PeriodConstant } from "../../../src/types/jobs/JobSchedule.type";
16+
import { Period } from "../../../src/types/jobs/JobSchedule.type";
17+
import { CronExpressionBuilder, CronPeriod, Month } from "../../../src/types/jobs/CronExpression.type";
1718

1819
jest.mock("../../../src/implementation/Client/HTTPClient/HTTPClient");
1920

@@ -28,16 +29,56 @@ describe("Jobs Client", () => {
2829
jobsClient = new HTTPClientJobs(httpClient)
2930
});
3031

31-
it("Should schedule", async () => {
32+
it("Should schedule using a period constant", async () => {
3233

3334
await jobsClient.schedule(
3435
"test",
3536
{
3637
some: "data",
3738
},
38-
PeriodConstant.Daily
39+
Period.Daily
3940
);
4041

4142
expect(httpClient.executeWithApiVersion).toHaveBeenCalledTimes(1);
43+
expect(httpClient.executeWithApiVersion).toHaveBeenCalledWith("v1.0-alpha1", "/jobs/test", {
44+
body: {
45+
data: { some: "data" },
46+
dueTime: null,
47+
repeats: null,
48+
schedule: "@daily",
49+
ttl: null,
50+
},
51+
headers: { "content-type": "application/json" },
52+
method: "POST",
53+
});
54+
});
55+
56+
it("Should schedule at the start of every month using a cron builder", async () => {
57+
58+
await jobsClient.schedule(
59+
"test",
60+
{
61+
some: "data",
62+
},
63+
CronExpressionBuilder
64+
.through(CronPeriod.Month, Month.January, Month.December)
65+
.on(CronPeriod.Second, 0)
66+
.on(CronPeriod.Minute, 0)
67+
.on(CronPeriod.Hour, 0)
68+
.on(CronPeriod.DayOfMonth, 1)
69+
);
70+
71+
expect(httpClient.executeWithApiVersion).toHaveBeenCalledTimes(1);
72+
expect(httpClient.executeWithApiVersion).toHaveBeenCalledWith("v1.0-alpha1", "/jobs/test", {
73+
body: {
74+
data: { some: "data" },
75+
dueTime: null,
76+
repeats: null,
77+
schedule: "0 0 0 1 JAN-DEC *",
78+
ttl: null,
79+
},
80+
headers: { "content-type": "application/json" },
81+
method: "POST",
82+
});
4283
});
4384
});

0 commit comments

Comments
 (0)