Skip to content

Commit 2ea0789

Browse files
committed
Added a cron job to ShelterSupply module to run every 30 minutes to change priority of expired supplies (urgent older than 4 hours)
1 parent 794c7b8 commit 2ea0789

File tree

8 files changed

+193
-1
lines changed

8 files changed

+193
-1
lines changed

package-lock.json

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"@nestjs/passport": "^10.0.3",
3131
"@nestjs/platform-express": "^10.0.0",
3232
"@nestjs/platform-fastify": "^10.3.8",
33+
"@nestjs/schedule": "^4.0.2",
3334
"@nestjs/swagger": "^7.3.1",
3435
"@prisma/client": "^5.13.0",
3536
"bcrypt": "^5.1.1",

src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
22
import { APP_INTERCEPTOR } from '@nestjs/core';
3+
import { ScheduleModule } from '@nestjs/schedule';
34

45
import { PrismaModule } from './prisma/prisma.module';
56
import { ShelterModule } from './shelter/shelter.module';
@@ -15,6 +16,7 @@ import { ShelterSupplyModule } from './shelter-supply/shelter-supply.module';
1516
@Module({
1617
imports: [
1718
PrismaModule,
19+
ScheduleModule.forRoot(),
1820
UsersModule,
1921
SessionsModule,
2022
ShelterModule,

src/shelter-supply/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const priorityExpiryInterval = 4; // In hours
2+
3+
export { priorityExpiryInterval };
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Injectable, Logger } from '@nestjs/common';
2+
import { Cron, CronExpression } from '@nestjs/schedule';
3+
import { PrismaService } from '../prisma/prisma.service';
4+
import { addHours } from '@/utils';
5+
import { priorityExpiryInterval } from './constants';
6+
import { SupplyPriority } from '../supply/types';
7+
import { ShelterSupplyService } from './shelter-supply.service';
8+
9+
@Injectable()
10+
export class ShelterSupplyJob {
11+
constructor(
12+
private readonly prismaService: PrismaService,
13+
private readonly shelterSupplyService: ShelterSupplyService,
14+
) {}
15+
16+
private readonly logger = new Logger(ShelterSupplyJob.name);
17+
18+
@Cron(CronExpression.EVERY_30_MINUTES)
19+
async handleCron() {
20+
this.logger.log(`${ShelterSupplyJob.name} running`);
21+
22+
const expiryDate = addHours(
23+
new Date(Date.now()),
24+
-priorityExpiryInterval,
25+
).toString();
26+
27+
const outatedSupplies = await this.prismaService.shelterSupply.findMany({
28+
where: {
29+
AND: [
30+
{ priority: SupplyPriority.Urgent },
31+
{
32+
OR: [
33+
{
34+
createdAt: {
35+
lte: expiryDate,
36+
},
37+
},
38+
{
39+
updatedAt: {
40+
lte: expiryDate,
41+
},
42+
},
43+
],
44+
},
45+
],
46+
},
47+
select: {
48+
supplyId: true,
49+
shelterId: true,
50+
},
51+
});
52+
53+
outatedSupplies.forEach(async (s) => {
54+
const { shelterId, supplyId } = s;
55+
await this.shelterSupplyService.update({
56+
data: {
57+
priority: SupplyPriority.Needing,
58+
},
59+
where: { shelterId: shelterId, supplyId: supplyId },
60+
});
61+
});
62+
}
63+
}

src/shelter-supply/shelter-supply.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ import { Module } from '@nestjs/common';
33
import { ShelterSupplyService } from './shelter-supply.service';
44
import { ShelterSupplyController } from './shelter-supply.controller';
55
import { PrismaModule } from '../prisma/prisma.module';
6+
import { ShelterSupplyJob } from './shelter-supply.job';
67

78
@Module({
89
imports: [PrismaModule],
9-
providers: [ShelterSupplyService],
10+
providers: [ShelterSupplyService, ShelterSupplyJob],
1011
controllers: [ShelterSupplyController],
1112
})
1213
export class ShelterSupplyModule {}

src/utils/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
getSessionData,
55
deepMerge,
66
capitalize,
7+
addHours,
78
} from './utils';
89

910
export {
@@ -12,4 +13,5 @@ export {
1213
removeNotNumbers,
1314
getSessionData,
1415
deepMerge,
16+
addHours,
1517
};

src/utils/utils.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,82 @@ function deepMerge(target: Record<string, any>, source: Record<string, any>) {
7575
}
7676
}
7777

78+
function parseStringToObject(path: string, value: any) {
79+
const keys = path.split('.');
80+
const currentObj = {};
81+
let temp = currentObj;
82+
83+
keys.forEach((key, index) => {
84+
if (index === keys.length - 1) {
85+
temp[key] = value;
86+
} else {
87+
temp[key] = {};
88+
temp = temp[key];
89+
}
90+
});
91+
92+
return currentObj;
93+
}
94+
95+
function getSearchWhere(search: string, or?: boolean) {
96+
const where = {};
97+
if (search !== '') {
98+
const terms = search.split(',');
99+
const groups: Record<string, { condition: string; mode?: string }[]> =
100+
terms.reduce((prev, current) => {
101+
const [key, condition, value] = current.split(':');
102+
const isBoolean = ['true', 'false'].includes(value);
103+
const mode =
104+
isBoolean || ['in', 'notIn'].includes(condition)
105+
? undefined
106+
: 'insensitive';
107+
const parsedValue = isBoolean ? value === 'true' : value;
108+
109+
return {
110+
...prev,
111+
[key]: [
112+
...(prev[key] ?? []),
113+
{
114+
[condition]: ['in', 'notIn'].includes(condition)
115+
? [parsedValue]
116+
: parsedValue,
117+
mode,
118+
},
119+
],
120+
};
121+
}, {});
122+
123+
const parsed = Object.entries(groups).reduce((prev, [key, current]) => {
124+
if (current.length > 1) {
125+
return {
126+
...prev,
127+
AND: current.map((c) => parseStringToObject(key, c)),
128+
};
129+
} else return deepMerge(prev, parseStringToObject(key, current[0]));
130+
}, {});
131+
Object.assign(where, parsed);
132+
}
133+
if (or) {
134+
return {
135+
OR: Object.entries(where).reduce(
136+
(prev, [key, value]) => [...prev, { [key]: value }],
137+
[] as any[],
138+
),
139+
};
140+
} else return where;
141+
}
142+
143+
function addHours(date: Date, hours: number) {
144+
const result = new Date();
145+
result.setTime(date.getTime() + hours * 60 * 60 * 1000);
146+
return result;
147+
}
148+
78149
export {
79150
ServerResponse,
80151
removeNotNumbers,
81152
getSessionData,
82153
deepMerge,
83154
capitalize,
155+
addHours,
84156
};

0 commit comments

Comments
 (0)