Skip to content

Commit a978c74

Browse files
authored
Merge pull request #21 from filipepacheco/rebase-develop
Rebase Develop onto main #2
2 parents 2895265 + 6b79159 commit a978c74

File tree

11 files changed

+297
-9
lines changed

11 files changed

+297
-9
lines changed

.env.local

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
TZ=America/Sao_Paulo
2+
3+
DB_HOST=sos-rs-db
4+
DB_PORT=5432
5+
DB_DATABASE_NAME=sos_rs
6+
DB_USER=root
7+
DB_PASSWORD=root
8+
DATABASE_URL="postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_DATABASE_NAME}?schema=public"
9+
SECRET_KEY=batata
10+
11+
HOST=::0.0.0.0
12+
PORT=4000

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Deploy Backend
22

33
on:
44
push:
5-
branches: [main]
5+
branches: [deploy]
66

77
jobs:
88
build:

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ lerna-debug.log*
4343
.env.development.local
4444
.env.test.local
4545
.env.production.local
46-
.env.local
46+
# .env.local
4747

4848
# temp directory
4949
.temp

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v18.19.1

docker-compose.dev.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
version: '3.8'
2+
3+
services:
4+
api:
5+
container_name: sos-rs-api
6+
image: node:18.18
7+
restart: always
8+
tty: true
9+
depends_on:
10+
- db
11+
ports:
12+
- '4000:4000'
13+
volumes:
14+
- .:/usr/app
15+
- /usr/app/node_modules
16+
working_dir: '/usr/app'
17+
environment:
18+
- DB_HOST=sos-rs-db
19+
- DB_PORT=5432
20+
- DB_DATABASE_NAME=sos_rs
21+
- DB_USER=root
22+
- DB_PASSWORD=root
23+
command: >
24+
sh -c "npm install &&
25+
npx prisma generate &&
26+
npx prisma migrate dev &&
27+
npm run start"
28+
db:
29+
container_name: sos-rs-db
30+
image: postgres
31+
environment:
32+
- POSTGRES_PASSWORD=root
33+
- POSTGRES_USER=root

src/prisma/prisma.service.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,37 @@
11
import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
2-
import { PrismaClient } from '@prisma/client';
2+
import { Prisma, PrismaClient } from '@prisma/client';
33
import * as bcrypt from 'bcrypt';
44

55
@Injectable()
6-
export class PrismaService extends PrismaClient<any> implements OnModuleInit {
6+
export class PrismaService
7+
extends PrismaClient<
8+
Prisma.PrismaClientOptions,
9+
'query' | 'info' | 'warn' | 'error' | 'beforeExit'
10+
>
11+
implements OnModuleInit
12+
{
13+
constructor() {
14+
super({
15+
log: [
16+
{
17+
emit: 'event',
18+
level: 'query',
19+
},
20+
{
21+
emit: 'event',
22+
level: 'error',
23+
},
24+
{
25+
emit: 'stdout',
26+
level: 'info',
27+
},
28+
{
29+
emit: 'stdout',
30+
level: 'warn',
31+
},
32+
],
33+
});
34+
}
735
async onModuleInit() {
836
await this.$connect();
937
this.$use(async (params, next) => {

src/shelter/shelter.controller.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
Param,
88
Post,
99
Put,
10+
Query,
1011
UseGuards,
1112
} from '@nestjs/common';
1213
import { ApiTags } from '@nestjs/swagger';
@@ -36,6 +37,17 @@ export class ShelterController {
3637
}
3738
}
3839

40+
@Get('/search')
41+
async search(@Query() complexSearchQueryParams) {
42+
try {
43+
const data = await this.shelterService.search(complexSearchQueryParams);
44+
return new ServerResponse(200, 'Successfully get shelters', data);
45+
} catch (err: any) {
46+
this.logger.error(`Failed to get shelters: ${err}`);
47+
throw new HttpException(err?.code ?? err?.name ?? `${err}`, 400);
48+
}
49+
}
50+
3951
@Get(':id')
4052
async show(@Param('id') id: string) {
4153
try {

src/shelter/shelter.service.ts

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { z } from 'zod';
22
import { Injectable } from '@nestjs/common';
33
import { Prisma } from '@prisma/client';
44
import { DefaultArgs } from '@prisma/client/runtime/library';
5-
65
import { PrismaService } from '../prisma/prisma.service';
76
import {
7+
ComplexSearchSchema,
88
CreateShelterSchema,
99
FullUpdateShelterSchema,
1010
UpdateShelterSchema,
@@ -70,7 +70,6 @@ export class ShelterService {
7070
prioritySum: true,
7171
latitude: true,
7272
longitude: true,
73-
verified: true,
7473
shelterSupplies: {
7574
select: {
7675
priority: true,
@@ -143,4 +142,175 @@ export class ShelterService {
143142
},
144143
);
145144
}
145+
146+
async search(props: z.infer<typeof ComplexSearchSchema>) {
147+
const payload = ComplexSearchSchema.parse({
148+
...props,
149+
supplyCategories:
150+
typeof props['supplyCategories[]'] === 'string'
151+
? [props['supplyCategories[]']]
152+
: props['supplyCategories[]'],
153+
supplies:
154+
typeof props['supplies[]'] === 'string'
155+
? [props['supplies[]']]
156+
: props['supplies[]'],
157+
});
158+
159+
const shelterStatusFilter = this.addShelterStatusFilter(payload);
160+
const where = this.mountWhereFilter(payload);
161+
const take = payload.perPage;
162+
const skip = payload.perPage * (payload.page - 1);
163+
164+
if (shelterStatusFilter.length > 0) {
165+
where['AND'].push({
166+
OR: shelterStatusFilter,
167+
});
168+
}
169+
170+
const count = await this.prismaService.shelter.count({
171+
where: where,
172+
});
173+
174+
const results = await this.prismaService.shelter.findMany({
175+
where: where,
176+
orderBy: {
177+
prioritySum: 'desc',
178+
},
179+
take,
180+
skip,
181+
select: {
182+
id: true,
183+
name: true,
184+
pix: true,
185+
address: true,
186+
capacity: true,
187+
contact: true,
188+
verified: true,
189+
petFriendly: true,
190+
shelteredPeople: true,
191+
prioritySum: true,
192+
latitude: true,
193+
longitude: true,
194+
createdAt: true,
195+
updatedAt: true,
196+
shelterSupplies: {
197+
where: {
198+
priority: {
199+
gte: SupplyPriority.Needing,
200+
},
201+
},
202+
take: 10,
203+
select: {
204+
priority: true,
205+
supply: {
206+
select: {
207+
name: true,
208+
},
209+
},
210+
},
211+
orderBy: {
212+
priority: 'desc',
213+
},
214+
},
215+
},
216+
});
217+
return { perPage: payload.perPage, page: payload.page, count, results };
218+
}
219+
220+
private mountWhereFilter(payload: z.infer<typeof ComplexSearchSchema>) {
221+
const filter: any = {
222+
AND: [
223+
{
224+
OR: [
225+
{ address: { contains: payload.search } },
226+
{ name: { contains: payload.search } },
227+
],
228+
},
229+
],
230+
};
231+
232+
const shelterSuppliesFilter = {
233+
shelterSupplies: {
234+
some: {},
235+
},
236+
};
237+
238+
if (payload.priority) {
239+
shelterSuppliesFilter.shelterSupplies.some['priority'] = parseInt(
240+
payload.priority,
241+
);
242+
}
243+
244+
if (payload?.supplyCategories && payload?.supplyCategories.length !== 0) {
245+
shelterSuppliesFilter.shelterSupplies.some['supply'] = {
246+
supplyCategoryId: {
247+
in: payload.supplyCategories,
248+
},
249+
};
250+
}
251+
252+
if (payload?.supplies && payload?.supplies.length !== 0) {
253+
shelterSuppliesFilter.shelterSupplies.some['supplyId'] = {
254+
in: payload.supplies,
255+
};
256+
}
257+
258+
if (Object.keys(shelterSuppliesFilter.shelterSupplies.some).length !== 0) {
259+
filter['AND'].push(shelterSuppliesFilter);
260+
}
261+
262+
return filter;
263+
}
264+
265+
private addShelterStatusFilter(payload: z.infer<typeof ComplexSearchSchema>) {
266+
const shelterStatusFilter: any = [];
267+
268+
if (payload.filterAvailableShelter) {
269+
shelterStatusFilter.push({
270+
AND: [
271+
{
272+
capacity: {
273+
gt: this.prismaService.shelter.fields.shelteredPeople,
274+
},
275+
},
276+
{
277+
capacity: { not: null },
278+
},
279+
{
280+
shelteredPeople: { not: null },
281+
},
282+
],
283+
});
284+
}
285+
286+
if (payload.filterUnavailableShelter) {
287+
shelterStatusFilter.push({
288+
AND: [
289+
{
290+
capacity: {
291+
lte: this.prismaService.shelter.fields.shelteredPeople,
292+
},
293+
},
294+
{
295+
capacity: { not: null },
296+
},
297+
{
298+
shelteredPeople: { not: null },
299+
},
300+
],
301+
});
302+
}
303+
304+
if (payload.waitingShelterAvailability) {
305+
shelterStatusFilter.push({
306+
capacity: null,
307+
});
308+
309+
shelterStatusFilter.push({
310+
shelteredPeople: null,
311+
});
312+
}
313+
314+
return shelterStatusFilter;
315+
}
146316
}

src/shelter/types.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,33 @@ const ShelterSchema = z.object({
2222
updatedAt: z.string().nullable().optional(),
2323
});
2424

25+
const ComplexSearchSchema = z.object({
26+
search: z.preprocess((v) => v ?? '', z.string()),
27+
status: z.array(z.number()).nullable().optional(),
28+
perPage: z.preprocess(
29+
(v) => +((v ?? '20') as string),
30+
z.number().min(1).max(100),
31+
),
32+
filterAvailableShelter: z.preprocess(
33+
(v) => (v === 'true' ? true : false),
34+
z.boolean(),
35+
),
36+
filterUnavailableShelter: z.preprocess(
37+
(v) => (v === 'true' ? true : false),
38+
z.boolean(),
39+
),
40+
waitingShelterAvailability: z.preprocess(
41+
(v) => (v === 'true' ? true : false),
42+
z.boolean(),
43+
),
44+
page: z.preprocess((v) => +((v ?? '1') as string), z.number().min(1)),
45+
supplyCategories: z.array(z.string()).nullable().optional(),
46+
supplies: z.array(z.string()).nullable().optional(),
47+
priority: z.string().nullable().optional(),
48+
order: z.string(),
49+
orderBy: z.string().nullable().optional(),
50+
});
51+
2552
const CreateShelterSchema = ShelterSchema.omit({
2653
id: true,
2754
createdAt: true,
@@ -44,4 +71,5 @@ export {
4471
CreateShelterSchema,
4572
UpdateShelterSchema,
4673
FullUpdateShelterSchema,
74+
ComplexSearchSchema,
4775
};

src/supply/supply.service.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,24 @@ export class SupplyService {
3333

3434
async index() {
3535
const data = await this.prismaService.supply.findMany({
36+
distinct: ['name', 'supplyCategoryId'],
37+
orderBy: {
38+
name: 'desc',
39+
},
3640
select: {
3741
id: true,
3842
name: true,
3943
supplyCategory: {
4044
select: {
4145
id: true,
4246
name: true,
43-
createdAt: true,
44-
updatedAt: true,
4547
},
4648
},
47-
updatedAt: true,
4849
createdAt: true,
50+
updatedAt: true,
4951
},
5052
});
53+
5154
return data;
5255
}
5356
}

0 commit comments

Comments
 (0)