savedjobs: criação de endpoint GET e ajuste do README#192
savedjobs: criação de endpoint GET e ajuste do README#192larisarapio wants to merge 50 commits intomainfrom
Conversation
…s. parte que faltava de autentificação em post foi ajustada
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/modules/savedjobs/services/find-all-savedjobs.service.ts (1)
8-8: Considere usar "Jobs" no plural para variáveis e classes.Um comentário anterior de revisão mencionou "Job > Jobs - pra variáveis". Recomendo manter consistência na nomenclatura utilizando o plural "Jobs" em todo o código.
🧰 Tools
🪛 ESLint
[error] 8-8: Delete
␍(prettier/prettier)
🧹 Nitpick comments (2)
src/modules/savedjobs/services/find-all-savedjobs.service.ts (2)
1-5: Padronize o uso de aspas simples para importações.Os padrões de estilo do projeto parecem favorecer o uso de aspas simples para importações, mas aqui estão sendo usadas aspas duplas.
-import { Injectable } from "@nestjs/common"; -import { SavedJobsEntity } from "src/database/entities/savedjobs.entity"; -import { PageDto, PageOptionsDto } from "src/shared/pagination"; -import { SavedJobsRepository } from "../repository/savedjobs.repository"; -import { GetAllSavedJobsDto } from "../dtos/get-all-savedjobs.dto"; +import { Injectable } from '@nestjs/common'; +import { SavedJobsEntity } from 'src/database/entities/savedjobs.entity'; +import { PageDto, PageOptionsDto } from 'src/shared/pagination'; +import { SavedJobsRepository } from '../repository/savedjobs.repository'; +import { GetAllSavedJobsDto } from '../dtos/get-all-savedjobs.dto';🧰 Tools
🪛 ESLint
[error] 1-1: Replace
"@nestjs/common";␍with'@nestjs/common';(prettier/prettier)
[error] 2-2: Replace
"src/database/entities/savedjobs.entity";␍with'src/database/entities/savedjobs.entity';(prettier/prettier)
[error] 3-3: Replace
"src/shared/pagination";␍with'src/shared/pagination';(prettier/prettier)
[error] 4-4: Replace
"../repository/savedjobs.repository";␍with'../repository/savedjobs.repository';(prettier/prettier)
[error] 5-5: Replace
"../dtos/get-all-savedjobs.dto";␍with'../dtos/get-all-savedjobs.dto';(prettier/prettier)
11-16: Bom trabalho na implementação do método execute.O método está corretamente tipado com os parâmetros
pageOptionsDtoefilters, e retorna uma Promise com o tipoPageDto<SavedJobsEntity>. A função atua como uma passagem direta para o repositório, o que segue o princípio de responsabilidade única.Considere adicionar tratamento de erros no nível do serviço para casos que o repositório não cobriu.
async execute( pageOptionsDto: PageOptionsDto, filters: GetAllSavedJobsDto, ): Promise<PageDto<SavedJobsEntity>> { - return this.savedJobsRepository.getAllSavedJobs(pageOptionsDto, filters); + try { + return await this.savedJobsRepository.getAllSavedJobs(pageOptionsDto, filters); + } catch (error) { + // Adicione tratamento específico se necessário ou apenas re-lance o erro + throw error; + } }🧰 Tools
🪛 ESLint
[error] 11-11: Delete
␍(prettier/prettier)
[error] 12-12: Delete
␍(prettier/prettier)
[error] 13-13: Delete
␍(prettier/prettier)
[error] 14-14: Delete
␍(prettier/prettier)
[error] 15-15: Delete
␍(prettier/prettier)
[error] 16-16: Delete
␍(prettier/prettier)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/modules/savedjobs/services/find-all-savedjobs.service.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/modules/savedjobs/services/find-all-savedjobs.service.ts (4)
src/modules/savedjobs/repository/savedjobs.repository.ts (1)
Injectable(8-44)src/shared/pagination/pageOptions.dto.ts (1)
PageOptionsDto(6-42)src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts (1)
GetAllSavedJobsDto(4-22)src/shared/pagination/page.dto.ts (1)
PageDto(4-14)
🪛 ESLint
src/modules/savedjobs/services/find-all-savedjobs.service.ts
[error] 1-1: Replace "@nestjs/common";␍ with '@nestjs/common';
(prettier/prettier)
[error] 2-2: Replace "src/database/entities/savedjobs.entity";␍ with 'src/database/entities/savedjobs.entity';
(prettier/prettier)
[error] 3-3: Replace "src/shared/pagination";␍ with 'src/shared/pagination';
(prettier/prettier)
[error] 4-4: Replace "../repository/savedjobs.repository";␍ with '../repository/savedjobs.repository';
(prettier/prettier)
[error] 5-5: Replace "../dtos/get-all-savedjobs.dto";␍ with '../dtos/get-all-savedjobs.dto';
(prettier/prettier)
[error] 6-6: Delete ␍
(prettier/prettier)
[error] 7-7: Delete ␍
(prettier/prettier)
[error] 8-8: Delete ␍
(prettier/prettier)
[error] 9-9: Delete ␍
(prettier/prettier)
[error] 10-10: Delete ␍
(prettier/prettier)
[error] 11-11: Delete ␍
(prettier/prettier)
[error] 12-12: Delete ␍
(prettier/prettier)
[error] 13-13: Delete ␍
(prettier/prettier)
[error] 14-14: Delete ␍
(prettier/prettier)
[error] 15-15: Delete ␍
(prettier/prettier)
[error] 16-16: Delete ␍
(prettier/prettier)
[error] 17-17: Insert ⏎
(prettier/prettier)
🔇 Additional comments (2)
src/modules/savedjobs/services/find-all-savedjobs.service.ts (2)
8-9: Service implementado adequadamente com injeção de dependência.A classe segue corretamente o padrão de injeção de dependência do NestJS com o decorator
@Injectable()e recebe o repositório no construtor.🧰 Tools
🪛 ESLint
[error] 8-8: Delete
␍(prettier/prettier)
[error] 9-9: Delete
␍(prettier/prettier)
1-17:Details
❓ Verification inconclusive
Avalie a necessidade de verificação de autorização.
O serviço não verifica se o usuário tem permissão para acessar as vagas salvas. Considere implementar verificações de autorização para garantir que um usuário só possa ver suas próprias vagas salvas, a menos que seja um administrador.
🏁 Script executed:
#!/bin/bash # Verificar se há verificações de autorização no controller correspondente # Procurar pelo controller que usa este serviço rg -A 10 "FindAllSavedJobsService" --type ts # Verificar se existem decorators de autorização nos arquivos do módulo rg "@UseGuards|@Roles|@RequirePermissions" --type ts src/modules/savedjobs/Length of output: 3937
Rever autorização na listagem de vagas salvas
No serviço
FindAllSavedJobsService(src/modules/savedjobs/services/find-all-savedjobs.service.ts, linhas 1-17) não há verificação interna de autorização. Embora o controller (src/modules/savedjobs/savedjobs.controller.ts) utilize@UseGuards(AuthGuard())para a rota de criação (exemplo: métodosaveJob), não foi possível confirmar que o endpoint que lista as vagas salvas está protegido de forma similar.
- Verifique se o endpoint que invoca
FindAllSavedJobsServiceimplementa proteção de autorização para garantir que apenas o usuário proprietário (ou um administrador) possa acessar as vagas salvas.- Caso a proteção já esteja sendo tratada em outro nível, confirme que todos os endpoints relevantes estão adequadamente protegidos.
🧰 Tools
🪛 ESLint
[error] 1-1: Replace
"@nestjs/common";␍with'@nestjs/common';(prettier/prettier)
[error] 2-2: Replace
"src/database/entities/savedjobs.entity";␍with'src/database/entities/savedjobs.entity';(prettier/prettier)
[error] 3-3: Replace
"src/shared/pagination";␍with'src/shared/pagination';(prettier/prettier)
[error] 4-4: Replace
"../repository/savedjobs.repository";␍with'../repository/savedjobs.repository';(prettier/prettier)
[error] 5-5: Replace
"../dtos/get-all-savedjobs.dto";␍with'../dtos/get-all-savedjobs.dto';(prettier/prettier)
[error] 6-6: Delete
␍(prettier/prettier)
[error] 7-7: Delete
␍(prettier/prettier)
[error] 8-8: Delete
␍(prettier/prettier)
[error] 9-9: Delete
␍(prettier/prettier)
[error] 10-10: Delete
␍(prettier/prettier)
[error] 11-11: Delete
␍(prettier/prettier)
[error] 12-12: Delete
␍(prettier/prettier)
[error] 13-13: Delete
␍(prettier/prettier)
[error] 14-14: Delete
␍(prettier/prettier)
[error] 15-15: Delete
␍(prettier/prettier)
[error] 16-16: Delete
␍(prettier/prettier)
[error] 17-17: Insert
⏎(prettier/prettier)
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/database/entities/savedjobs.entity.ts (1)
21-22:⚠️ Potential issuePor que o jobId está configurado como nullable?
O campo
jobIdestá definido comonullable: true, o que permite salvar empregos sem um ID de job válido. Isso parece contraditório com o propósito da entidade, já que um "emprego salvo" sem referência a um emprego real não faz sentido do ponto de vista de negócio.Além disso, não há garantia de integridade referencial, pois o campo está definido como uma simples coluna UUID em vez de uma relação com a entidade de empregos.
Sugestão:
- Altere para
nullable: falseou- Preferivelmente, transforme em uma relação ManyToOne com a entidade Jobs:
- @Column('uuid', { nullable: true }) - jobId: string; + @ManyToOne(() => JobsEntity, { onDelete: 'CASCADE', nullable: false }) + @JoinColumn({ name: 'jobId' }) + job: JobsEntity;Isso garantirá a integridade referencial e impedirá a criação de registros sem um job válido.
🧰 Tools
🪛 ESLint
[error] 21-21: Delete
··(prettier/prettier)
[error] 22-22: Replace
····with··(prettier/prettier)
🧹 Nitpick comments (4)
src/database/entities/savedjobs.entity.ts (4)
1-33: Adicione índices para melhorar a performance de consultasA entidade não possui índices definidos além da chave primária. Para melhorar o desempenho das consultas, especialmente ao buscar empregos salvos por jobId ou userId, é recomendável adicionar índices apropriados.
@Entity('tb_saved_jobs') + @Index(['jobId']) + @Index(['user']) export class SavedJobsEntity { // restante do código }Não se esqueça de importar o decorador
Indexdo typeorm:import { Column, CreateDateColumn, Entity, + Index, ManyToOne, PrimaryGeneratedColumn, } from 'typeorm';🧰 Tools
🪛 ESLint
[error] 2-2: Delete
··(prettier/prettier)
[error] 3-3: Delete
··(prettier/prettier)
[error] 4-4: Replace
····with··(prettier/prettier)
[error] 5-5: Delete
··(prettier/prettier)
[error] 6-6: Replace
····with··(prettier/prettier)
[error] 7-7: Delete
··(prettier/prettier)
[error] 8-8: Delete
··(prettier/prettier)
[error] 9-9: Delete
··(prettier/prettier)
[error] 10-10: Delete
··(prettier/prettier)
[error] 11-11: Delete
··(prettier/prettier)
[error] 12-12: Replace
····with··(prettier/prettier)
[error] 13-13: Delete
··(prettier/prettier)
[error] 14-14: Delete
··(prettier/prettier)
[error] 15-15: Delete
··(prettier/prettier)
[error] 16-16: Delete
··(prettier/prettier)
[error] 17-17: Delete
··(prettier/prettier)
[error] 18-18: Replace
····with··(prettier/prettier)
[error] 19-19: Delete
··(prettier/prettier)
[error] 20-20: Delete
··(prettier/prettier)
[error] 21-21: Delete
··(prettier/prettier)
[error] 22-22: Replace
····with··(prettier/prettier)
[error] 23-23: Delete
··(prettier/prettier)
[error] 24-24: Replace
····with··(prettier/prettier)
[error] 25-25: Delete
··(prettier/prettier)
[error] 26-26: Delete
··(prettier/prettier)
[error] 27-27: Delete
··(prettier/prettier)
[error] 28-28: Replace
······with····(prettier/prettier)
[error] 29-29: Delete
··(prettier/prettier)
[error] 30-30: Replace
······with····(prettier/prettier)
[error] 31-31: Delete
··(prettier/prettier)
[error] 32-32: Delete
··(prettier/prettier)
[error] 33-33: Replace
··}with}⏎(prettier/prettier)
1-33: Adicione documentação para a entidade e seus camposA entidade não possui documentação explicando seu propósito e o significado de seus campos. Adicionar comentários JSDoc melhoraria a compreensão do código para outros desenvolvedores.
Exemplo:
/** + * Entidade que representa um emprego salvo por um usuário. + * Esta tabela armazena as relações entre usuários e vagas que eles salvaram. + */ @Entity('tb_saved_jobs') export class SavedJobsEntity { @PrimaryGeneratedColumn('uuid') + /** Identificador único do registro de emprego salvo */ id: string; @ManyToOne(() => UsersEntity, (user) => user.savedJobs, { onDelete: 'CASCADE', nullable: false, }) + /** Usuário que salvou o emprego */ user: UsersEntity; // Outros campos... }🧰 Tools
🪛 ESLint
[error] 2-2: Delete
··(prettier/prettier)
[error] 3-3: Delete
··(prettier/prettier)
[error] 4-4: Replace
····with··(prettier/prettier)
[error] 5-5: Delete
··(prettier/prettier)
[error] 6-6: Replace
····with··(prettier/prettier)
[error] 7-7: Delete
··(prettier/prettier)
[error] 8-8: Delete
··(prettier/prettier)
[error] 9-9: Delete
··(prettier/prettier)
[error] 10-10: Delete
··(prettier/prettier)
[error] 11-11: Delete
··(prettier/prettier)
[error] 12-12: Replace
····with··(prettier/prettier)
[error] 13-13: Delete
··(prettier/prettier)
[error] 14-14: Delete
··(prettier/prettier)
[error] 15-15: Delete
··(prettier/prettier)
[error] 16-16: Delete
··(prettier/prettier)
[error] 17-17: Delete
··(prettier/prettier)
[error] 18-18: Replace
····with··(prettier/prettier)
[error] 19-19: Delete
··(prettier/prettier)
[error] 20-20: Delete
··(prettier/prettier)
[error] 21-21: Delete
··(prettier/prettier)
[error] 22-22: Replace
····with··(prettier/prettier)
[error] 23-23: Delete
··(prettier/prettier)
[error] 24-24: Replace
····with··(prettier/prettier)
[error] 25-25: Delete
··(prettier/prettier)
[error] 26-26: Delete
··(prettier/prettier)
[error] 27-27: Delete
··(prettier/prettier)
[error] 28-28: Replace
······with····(prettier/prettier)
[error] 29-29: Delete
··(prettier/prettier)
[error] 30-30: Replace
······with····(prettier/prettier)
[error] 31-31: Delete
··(prettier/prettier)
[error] 32-32: Delete
··(prettier/prettier)
[error] 33-33: Replace
··}with}⏎(prettier/prettier)
1-33: Corrija a formatação do código para seguir o padrão do projetoO código contém problemas de formatação como identificado pelas ferramentas de análise estática. Certifique-se de usar a configuração de formatação correta e execute o prettier para formatar o arquivo adequadamente.
Execute o seguinte comando para corrigir automaticamente os problemas de formatação:
npm run formatou
yarn format🧰 Tools
🪛 ESLint
[error] 2-2: Delete
··(prettier/prettier)
[error] 3-3: Delete
··(prettier/prettier)
[error] 4-4: Replace
····with··(prettier/prettier)
[error] 5-5: Delete
··(prettier/prettier)
[error] 6-6: Replace
····with··(prettier/prettier)
[error] 7-7: Delete
··(prettier/prettier)
[error] 8-8: Delete
··(prettier/prettier)
[error] 9-9: Delete
··(prettier/prettier)
[error] 10-10: Delete
··(prettier/prettier)
[error] 11-11: Delete
··(prettier/prettier)
[error] 12-12: Replace
····with··(prettier/prettier)
[error] 13-13: Delete
··(prettier/prettier)
[error] 14-14: Delete
··(prettier/prettier)
[error] 15-15: Delete
··(prettier/prettier)
[error] 16-16: Delete
··(prettier/prettier)
[error] 17-17: Delete
··(prettier/prettier)
[error] 18-18: Replace
····with··(prettier/prettier)
[error] 19-19: Delete
··(prettier/prettier)
[error] 20-20: Delete
··(prettier/prettier)
[error] 21-21: Delete
··(prettier/prettier)
[error] 22-22: Replace
····with··(prettier/prettier)
[error] 23-23: Delete
··(prettier/prettier)
[error] 24-24: Replace
····with··(prettier/prettier)
[error] 25-25: Delete
··(prettier/prettier)
[error] 26-26: Delete
··(prettier/prettier)
[error] 27-27: Delete
··(prettier/prettier)
[error] 28-28: Replace
······with····(prettier/prettier)
[error] 29-29: Delete
··(prettier/prettier)
[error] 30-30: Replace
······with····(prettier/prettier)
[error] 31-31: Delete
··(prettier/prettier)
[error] 32-32: Delete
··(prettier/prettier)
[error] 33-33: Replace
··}with}⏎(prettier/prettier)
24-25: Considere adicionar campo updatedAt para rastreamento de modificaçõesA entidade possui apenas o campo
savedAt, mas não tem um campo para rastrear atualizações. Embora possa não ser essencial para esta entidade específica (se você não espera que os registros sejam atualizados), é uma boa prática incluir um campoupdatedAtpara manter consistência com outras entidades e facilitar auditorias futuras.@CreateDateColumn() savedAt: Date; + @UpdateDateColumn() + updatedAt: Date;Não se esqueça de adicionar o import:
import { Column, CreateDateColumn, + UpdateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, } from 'typeorm';🧰 Tools
🪛 ESLint
[error] 24-24: Replace
····with··(prettier/prettier)
[error] 25-25: Delete
··(prettier/prettier)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/database/entities/savedjobs.entity.ts(1 hunks)
🧰 Additional context used
🪛 ESLint
src/database/entities/savedjobs.entity.ts
[error] 2-2: Delete ··
(prettier/prettier)
[error] 3-3: Delete ··
(prettier/prettier)
[error] 4-4: Replace ···· with ··
(prettier/prettier)
[error] 5-5: Delete ··
(prettier/prettier)
[error] 6-6: Replace ···· with ··
(prettier/prettier)
[error] 7-7: Delete ··
(prettier/prettier)
[error] 8-8: Delete ··
(prettier/prettier)
[error] 9-9: Delete ··
(prettier/prettier)
[error] 10-10: Delete ··
(prettier/prettier)
[error] 11-11: Delete ··
(prettier/prettier)
[error] 12-12: Replace ···· with ··
(prettier/prettier)
[error] 13-13: Delete ··
(prettier/prettier)
[error] 14-14: Delete ··
(prettier/prettier)
[error] 15-15: Delete ··
(prettier/prettier)
[error] 16-16: Delete ··
(prettier/prettier)
[error] 17-17: Delete ··
(prettier/prettier)
[error] 18-18: Replace ···· with ··
(prettier/prettier)
[error] 19-19: Delete ··
(prettier/prettier)
[error] 20-20: Delete ··
(prettier/prettier)
[error] 21-21: Delete ··
(prettier/prettier)
[error] 22-22: Replace ···· with ··
(prettier/prettier)
[error] 23-23: Delete ··
(prettier/prettier)
[error] 24-24: Replace ···· with ··
(prettier/prettier)
[error] 25-25: Delete ··
(prettier/prettier)
[error] 26-26: Delete ··
(prettier/prettier)
[error] 27-27: Delete ··
(prettier/prettier)
[error] 28-28: Replace ······ with ····
(prettier/prettier)
[error] 29-29: Delete ··
(prettier/prettier)
[error] 30-30: Replace ······ with ····
(prettier/prettier)
[error] 31-31: Delete ··
(prettier/prettier)
[error] 32-32: Delete ··
(prettier/prettier)
[error] 33-33: Replace ··} with }⏎
(prettier/prettier)
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts (1)
11-11: Tipo de resposta incorreto para a criação bem-sucedida.O
NotFoundSwaggerestá sendo usado para representar uma resposta de sucesso (status 201), o que é semanticamente incorreto. A resposta de sucesso deve utilizar um tipo que represente a estrutura dos dados retornados quando uma vaga é salva com sucesso.- type: NotFoundSwagger, + type: SavedJobResponseSwagger, // Substitua pelo tipo que representa uma vaga salva com sucessoRecomendo criar uma classe específica como
SavedJobResponseSwaggerque reflita a estrutura de uma vaga salva.🧰 Tools
🪛 ESLint
[error] 11-11: Delete
␍(prettier/prettier)
🧹 Nitpick comments (3)
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts (1)
19-19: Remover espaços extras.Existem espaços extras após a vírgula que devem ser removidos para manter a consistência no código.
- status: HttpStatus.CONFLICT, + status: HttpStatus.CONFLICT,🧰 Tools
🪛 ESLint
[error] 19-19: Delete
··␍(prettier/prettier)
src/modules/savedjobs/services/savedjobs.service.ts (2)
29-30: Ajuste de formatação sugerido pelo Prettier.
A mensagem de exceção precisa de uma formatação específica indicada pelo lint para manter o padrão do projeto.- throw new BadRequestException('O ID do usuário e o ID da vaga devem ser fornecidos'); + throw new BadRequestException( + 'O ID do usuário e o ID da vaga devem ser fornecidos', + );🧰 Tools
🪛 ESLint
[error] 30-30: Replace
'O·ID·do·usuário·e·o·ID·da·vaga·devem·ser·fornecidos'with⏎········'O·ID·do·usuário·e·o·ID·da·vaga·devem·ser·fornecidos',⏎······(prettier/prettier)
43-45: Inserir vírgula após a chavejobId.
Segundo o relatório do ESLint/Prettier, falta uma vírgula para manter o padrão de formatação.where: { user: user, jobId } + where: { user: user, jobId },🧰 Tools
🪛 ESLint
[error] 44-44: Insert
,(prettier/prettier)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/modules/savedjobs/services/savedjobs.service.ts(1 hunks)src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts (2)
src/shared/Swagger/not-found.swagger.ts (1)
NotFoundSwagger(2-5)src/shared/Swagger/bad-request.swagger.ts (1)
BadRequestSwagger(2-5)
src/modules/savedjobs/services/savedjobs.service.ts (2)
src/modules/savedjobs/repository/savedjobs.repository.ts (1)
Injectable(8-44)src/modules/savedjobs/dtos/create-savedJob-dto.ts (1)
CreateSavedJobDto(3-11)
🪛 ESLint
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts
[error] 1-1: Replace "@nestjs/common";␍ with '@nestjs/common';
(prettier/prettier)
[error] 2-2: Replace "@nestjs/swagger";␍ with '@nestjs/swagger';
(prettier/prettier)
[error] 3-3: Replace "../../not-found.swagger";␍ with '../../not-found.swagger';
(prettier/prettier)
[error] 4-4: Replace "../../bad-request.swagger";␍ with '../../bad-request.swagger';
(prettier/prettier)
[error] 5-5: Delete ␍
(prettier/prettier)
[error] 6-6: Delete ␍
(prettier/prettier)
[error] 7-7: Delete ␍
(prettier/prettier)
[error] 8-8: Delete ␍
(prettier/prettier)
[error] 9-9: Delete ␍
(prettier/prettier)
[error] 10-10: Replace ·'Cadastro·realizado·com·sucesso.·Retorna·os·dados·do·novo·registro.',␍ with ⏎········'Cadastro·realizado·com·sucesso.·Retorna·os·dados·do·novo·registro.',
(prettier/prettier)
[error] 11-11: Delete ␍
(prettier/prettier)
[error] 12-12: Delete ␍
(prettier/prettier)
[error] 13-13: Delete ␍
(prettier/prettier)
[error] 14-14: Delete ␍
(prettier/prettier)
[error] 15-15: Replace ·'Requisição·inválida.·Verifique·os·campos·enviados·no·corpo·da·requisição.',␍ with ⏎········'Requisição·inválida.·Verifique·os·campos·enviados·no·corpo·da·requisição.',
(prettier/prettier)
[error] 16-16: Delete ␍
(prettier/prettier)
[error] 17-17: Delete ␍
(prettier/prettier)
[error] 18-18: Delete ␍
(prettier/prettier)
[error] 19-19: Delete ··␍
(prettier/prettier)
[error] 20-20: Delete ␍
(prettier/prettier)
[error] 21-21: Delete ␍
(prettier/prettier)
[error] 22-22: Delete ␍
(prettier/prettier)
[error] 23-23: Delete ␍
(prettier/prettier)
[error] 24-24: Replace ········description:·'Endpoint·responsável·por·criar·uma·vaga·salva.·Requer·dados·obrigatórios·no·corpo·da·requisição.',␍ with ······description:⏎········'Endpoint·responsável·por·criar·uma·vaga·salva.·Requer·dados·obrigatórios·no·corpo·da·requisição.',
(prettier/prettier)
[error] 25-25: Delete ␍
(prettier/prettier)
[error] 26-26: Delete ␍
(prettier/prettier)
[error] 27-27: Delete ␍
(prettier/prettier)
src/modules/savedjobs/services/savedjobs.service.ts
[error] 30-30: Replace 'O·ID·do·usuário·e·o·ID·da·vaga·devem·ser·fornecidos' with ⏎········'O·ID·do·usuário·e·o·ID·da·vaga·devem·ser·fornecidos',⏎······
(prettier/prettier)
[error] 44-44: Insert ,
(prettier/prettier)
🔇 Additional comments (2)
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts (1)
6-27: Implementar validação para evitar duplicações.Conforme sugerido em um comentário anterior, é importante validar se a vaga já foi salva pelo usuário antes de salvar novamente. Embora esta validação deva ser implementada no controlador ou serviço, é bom garantir que a documentação Swagger reflita corretamente este comportamento, como já está fazendo com a resposta 409.
Observe que o endpoint já prevê o status 409 para o caso de vaga já salva, o que é correto.
🧰 Tools
🪛 ESLint
[error] 6-6: Delete
␍(prettier/prettier)
[error] 7-7: Delete
␍(prettier/prettier)
[error] 8-8: Delete
␍(prettier/prettier)
[error] 9-9: Delete
␍(prettier/prettier)
[error] 10-10: Replace
·'Cadastro·realizado·com·sucesso.·Retorna·os·dados·do·novo·registro.',␍with⏎········'Cadastro·realizado·com·sucesso.·Retorna·os·dados·do·novo·registro.',(prettier/prettier)
[error] 11-11: Delete
␍(prettier/prettier)
[error] 12-12: Delete
␍(prettier/prettier)
[error] 13-13: Delete
␍(prettier/prettier)
[error] 14-14: Delete
␍(prettier/prettier)
[error] 15-15: Replace
·'Requisição·inválida.·Verifique·os·campos·enviados·no·corpo·da·requisição.',␍with⏎········'Requisição·inválida.·Verifique·os·campos·enviados·no·corpo·da·requisição.',(prettier/prettier)
[error] 16-16: Delete
␍(prettier/prettier)
[error] 17-17: Delete
␍(prettier/prettier)
[error] 18-18: Delete
␍(prettier/prettier)
[error] 19-19: Delete
··␍(prettier/prettier)
[error] 20-20: Delete
␍(prettier/prettier)
[error] 21-21: Delete
␍(prettier/prettier)
[error] 22-22: Delete
␍(prettier/prettier)
[error] 23-23: Delete
␍(prettier/prettier)
[error] 24-24: Replace
········description:·'Endpoint·responsável·por·criar·uma·vaga·salva.·Requer·dados·obrigatórios·no·corpo·da·requisição.',␍with······description:⏎········'Endpoint·responsável·por·criar·uma·vaga·salva.·Requer·dados·obrigatórios·no·corpo·da·requisição.',(prettier/prettier)
[error] 25-25: Delete
␍(prettier/prettier)
[error] 26-26: Delete
␍(prettier/prettier)
[error] 27-27: Delete
␍(prettier/prettier)
src/modules/savedjobs/services/savedjobs.service.ts (1)
1-22: Ótima separação de responsabilidades e uso correto dos repositórios.
A declaração do serviço com o decorador@Injectable()e a injeção de dependências via constructor estão corretas e bem estruturadas.
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts
Outdated
Show resolved
Hide resolved
| import { applyDecorators, HttpStatus } from "@nestjs/common"; | ||
| import { ApiOperation, ApiResponse } from "@nestjs/swagger"; | ||
| import { NotFoundSwagger } from "../../not-found.swagger"; | ||
| import { BadRequestSwagger } from "../../bad-request.swagger"; | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Falta importação para o tipo de resposta de sucesso.
Está faltando a importação de um tipo adequado para representar a resposta de sucesso da criação de uma vaga salva.
import { applyDecorators, HttpStatus } from "@nestjs/common";
import { ApiOperation, ApiResponse } from "@nestjs/swagger";
import { NotFoundSwagger } from "../../not-found.swagger";
import { BadRequestSwagger } from "../../bad-request.swagger";
+import { SavedJobResponseSwagger } from "../../saved-job-response.swagger"; // Importe o tipo correto📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { applyDecorators, HttpStatus } from "@nestjs/common"; | |
| import { ApiOperation, ApiResponse } from "@nestjs/swagger"; | |
| import { NotFoundSwagger } from "../../not-found.swagger"; | |
| import { BadRequestSwagger } from "../../bad-request.swagger"; | |
| import { applyDecorators, HttpStatus } from "@nestjs/common"; | |
| import { ApiOperation, ApiResponse } from "@nestjs/swagger"; | |
| import { NotFoundSwagger } from "../../not-found.swagger"; | |
| import { BadRequestSwagger } from "../../bad-request.swagger"; | |
| import { SavedJobResponseSwagger } from "../../saved-job-response.swagger"; // Importe o tipo correto |
🧰 Tools
🪛 ESLint
[error] 1-1: Replace "@nestjs/common";␍ with '@nestjs/common';
(prettier/prettier)
[error] 2-2: Replace "@nestjs/swagger";␍ with '@nestjs/swagger';
(prettier/prettier)
[error] 3-3: Replace "../../not-found.swagger";␍ with '../../not-found.swagger';
(prettier/prettier)
[error] 4-4: Replace "../../bad-request.swagger";␍ with '../../bad-request.swagger';
(prettier/prettier)
[error] 5-5: Delete ␍
(prettier/prettier)
| const existingSavedJob = await this.savedJobsRepository.findOne({ | ||
| where: { user: user, jobId } | ||
| }); | ||
|
|
||
| if (existingSavedJob) { | ||
| throw new BadRequestException('Este trabalho já foi salvo pelo usuário'); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Prevenir condições de corrida ao salvar vagas duplicadas.
Ao verificar se o usuário já salvou a vaga, ainda pode haver problemas de duplicidade em cenários concorrentes (por exemplo, se duas requisições simultâneas passarem pela checagem antes de salvar). Sugere-se criar uma constraint de unicidade no banco para (userId, jobId) ou envolver a lógica em transação para proteção contra duplicações concorrentes.
🧰 Tools
🪛 ESLint
[error] 44-44: Insert ,
(prettier/prettier)
There was a problem hiding this comment.
Actionable comments posted: 10
♻️ Duplicate comments (2)
src/database/entities/savedjobs.entity.ts (1)
23-24: 🛠️ Refactor suggestionConsidere tornar jobId não nulo.
O campo
jobIdestá configurado como nullable, mas isso parece contradizer o propósito da entidade. Como um usuário poderia salvar uma vaga sem o ID da vaga? Recomendo alterar para não-nulo para manter a integridade dos dados.-@Column('uuid', { nullable: true }) +@Column('uuid', { nullable: false }) jobId: string;🧰 Tools
🪛 ESLint
[error] 23-23: Delete
··(prettier/prettier)
[error] 24-24: Delete
··(prettier/prettier)
src/modules/savedjobs/savedjobs.controller.ts (1)
43-60: 🛠️ Refactor suggestionUtilize o usuário autenticado para filtrar as vagas salvas.
O endpoint
getAllSavedJobsestá protegido comAuthGuard(), mas não utiliza o usuário autenticado para filtrar as vagas. É mais seguro e correto mostrar apenas as vagas salvas do usuário autenticado.@Get() @ApiBearerAuth() @UseGuards(AuthGuard()) @SwaggerFindSavedJobs() @ApiOperation({ summary: 'Obtenha todos os trabalhos salvos com filtros e paginação.' }) async getAllSavedJobs( @Query() pageOptionsDto: PageOptionsDto, @Query() query: GetAllSavedJobsDto, + @GetUser() user: UsersEntity, ): Promise<PageDto<SavedJobsEntity>> { try { - return await this.findAllSavedJobsService.execute(pageOptionsDto, query); + return this.findAllSavedJobsService.execute(pageOptionsDto, { + ...query, + userId: user.id + }); } catch (error) { throw new HttpException( 'Erro ao buscar vagas salvas', HttpStatus.INTERNAL_SERVER_ERROR, ); } }Certifique-se de que o DTO
GetAllSavedJobsDtotenha o campouserIdopcional:export class GetAllSavedJobsDto { + @IsOptional() + @IsUUID() + userId?: string; // outros campos... }🧰 Tools
🪛 ESLint
[error] 47-47: Replace
·summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.'with⏎····summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.',⏎·(prettier/prettier)
🧹 Nitpick comments (3)
src/modules/savedjobs/services/savedjobs.service.ts (3)
45-51: Implementação correta para evitar duplicações de vagas salvas.A verificação de vagas já salvas pelo usuário e o lançamento de exceção apropriada é uma boa abordagem para evitar duplicações. Considere adicionar também uma restrição de unicidade no nível do banco de dados para prevenir condições de corrida em ambientes de alta concorrência.
Para garantir ainda mais proteção contra duplicidades em cenários concorrentes, você poderia envolver a operação em uma transação ou implementar uma restrição única no banco de dados para o par (userId, jobId).
🧰 Tools
🪛 ESLint
[error] 46-46: Insert
,(prettier/prettier)
54-59: Remova a linha em branco desnecessária.Há uma linha em branco desnecessária entre a criação do objeto e suas propriedades.
const newSavedJob = this.savedJobsRepository.create({ - user, jobId, savedAt: new Date(), });🧰 Tools
🪛 ESLint
[error] 54-55: Delete
⏎······(prettier/prettier)
29-38: Verificação de entrada redundante.A verificação de
!userId || !jobIdé redundante, pois o DTO já tem decoradores@IsNotEmpty()que validam isso. Se os decoradores estiverem funcionando corretamente com a validação global do NestJS, essa verificação adicional não seria necessária.🧰 Tools
🪛 ESLint
[error] 30-30: Replace
'O·ID·do·usuário·e·o·ID·da·vaga·devem·ser·fornecidos'with⏎········'O·ID·do·usuário·e·o·ID·da·vaga·devem·ser·fornecidos',⏎······(prettier/prettier)
[error] 36-36: Delete
······(prettier/prettier)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/database/entities/savedjobs.entity.ts(1 hunks)src/modules/auth/services/auth-login.service.ts(1 hunks)src/modules/savedjobs/savedjobs.controller.ts(1 hunks)src/modules/savedjobs/services/savedjobs.service.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/database/entities/savedjobs.entity.ts (1)
src/database/entities/users.entity.ts (1)
Entity(22-108)
src/modules/savedjobs/services/savedjobs.service.ts (2)
src/modules/savedjobs/repository/savedjobs.repository.ts (1)
Injectable(8-44)src/modules/savedjobs/dtos/create-savedJob-dto.ts (1)
CreateSavedJobDto(3-11)
🪛 ESLint
src/database/entities/savedjobs.entity.ts
[error] 2-2: Delete ··
(prettier/prettier)
[error] 3-3: Delete ··
(prettier/prettier)
[error] 4-4: Replace ···· with ··
(prettier/prettier)
[error] 5-5: Delete ··
(prettier/prettier)
[error] 6-6: Replace ···· with ··
(prettier/prettier)
[error] 7-7: Delete ··
(prettier/prettier)
[error] 8-8: Delete ··
(prettier/prettier)
[error] 9-9: Delete ··
(prettier/prettier)
[error] 10-10: Delete ··
(prettier/prettier)
[error] 11-11: Delete ··
(prettier/prettier)
[error] 12-12: Delete ··
(prettier/prettier)
[error] 13-13: Replace ···· with ··
(prettier/prettier)
[error] 14-14: Delete ··
(prettier/prettier)
[error] 15-15: Delete ··
(prettier/prettier)
[error] 16-16: Delete ··
(prettier/prettier)
[error] 17-17: Delete ··
(prettier/prettier)
[error] 18-18: Delete ··
(prettier/prettier)
[error] 19-19: Replace ···· with ··
(prettier/prettier)
[error] 20-20: Delete ··
(prettier/prettier)
[error] 21-21: Delete ··
(prettier/prettier)
[error] 22-22: Delete ··
(prettier/prettier)
[error] 23-23: Delete ··
(prettier/prettier)
[error] 24-24: Delete ··
(prettier/prettier)
[error] 25-25: Delete ··
(prettier/prettier)
[error] 26-26: Delete ··
(prettier/prettier)
[error] 27-27: Delete ··
(prettier/prettier)
[error] 28-28: Delete ··
(prettier/prettier)
[error] 29-29: Delete ··
(prettier/prettier)
[error] 30-30: Replace ······ with ····
(prettier/prettier)
[error] 31-31: Delete ··
(prettier/prettier)
[error] 32-32: Replace ······ with ····
(prettier/prettier)
[error] 33-33: Delete ··
(prettier/prettier)
[error] 34-34: Delete ··
(prettier/prettier)
[error] 35-35: Replace ··} with }⏎
(prettier/prettier)
src/modules/savedjobs/savedjobs.controller.ts
[error] 1-1: Replace ·Body,·Controller,·Get,·HttpException,·HttpStatus,·Post,·Query,·UseGuards· with ⏎··Body,⏎··Controller,⏎··Get,⏎··HttpException,⏎··HttpStatus,⏎··Post,⏎··Query,⏎··UseGuards,⏎
(prettier/prettier)
[error] 12-12: 'log' is defined but never used.
(@typescript-eslint/no-unused-vars)
[error] 14-15: Delete ⏎
(prettier/prettier)
[error] 25-25: Delete ·
(prettier/prettier)
[error] 33-33: Delete ······
(prettier/prettier)
[error] 47-47: Replace ·summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.' with ⏎····summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.',⏎·
(prettier/prettier)
src/modules/savedjobs/services/savedjobs.service.ts
[error] 30-30: Replace 'O·ID·do·usuário·e·o·ID·da·vaga·devem·ser·fornecidos' with ⏎········'O·ID·do·usuário·e·o·ID·da·vaga·devem·ser·fornecidos',⏎······
(prettier/prettier)
[error] 36-36: Delete ······
(prettier/prettier)
[error] 46-46: Insert ,
(prettier/prettier)
[error] 52-52: Insert ;
(prettier/prettier)
[error] 54-55: Delete ⏎······
(prettier/prettier)
🔇 Additional comments (1)
src/database/entities/savedjobs.entity.ts (1)
16-21: LGTM! Relação com cascata para exclusão.A configuração da relação com
UsersEntityestá correta, com cascata para exclusão (onDelete: 'CASCADE') e não permitindo valores nulos (nullable: false), o que garante que quando um usuário for excluído, todas as suas vagas salvas também serão removidas.🧰 Tools
🪛 ESLint
[error] 16-16: Delete
··(prettier/prettier)
[error] 17-17: Delete
··(prettier/prettier)
[error] 18-18: Delete
··(prettier/prettier)
[error] 19-19: Replace
····with··(prettier/prettier)
[error] 20-20: Delete
··(prettier/prettier)
[error] 21-21: Delete
··(prettier/prettier)
| @Entity('tb_saved_jobs') | ||
| export class SavedJobsEntity { | ||
| @PrimaryGeneratedColumn('uuid') | ||
| id: string; | ||
|
|
||
| @ManyToOne(() => UsersEntity, (user) => user.savedJobs, { | ||
| onDelete: 'CASCADE', | ||
| nullable: false, | ||
| }) | ||
| @JoinColumn({ name: 'userId' }) | ||
| user: UsersEntity; | ||
|
|
||
| @Column('uuid', { nullable: true }) | ||
| jobId: string; | ||
|
|
||
| @CreateDateColumn() | ||
| savedAt: Date; | ||
|
|
||
| constructor(savedJob?: Partial<SavedJobsEntity>) { | ||
| this.id = savedJob?.id; | ||
| this.user = savedJob?.user; | ||
| this.jobId = savedJob?.jobId; | ||
| this.savedAt = savedJob?.savedAt; | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Adicione restrição de unicidade para evitar duplicações.
Não há uma restrição de unicidade para evitar que um usuário salve a mesma vaga várias vezes. Embora a validação seja feita no serviço, é uma boa prática adicionar essa restrição também na camada de banco de dados.
@Entity('tb_saved_jobs')
export class SavedJobsEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@ManyToOne(() => UsersEntity, (user) => user.savedJobs, {
onDelete: 'CASCADE',
nullable: false,
})
@JoinColumn({ name: 'userId' })
user: UsersEntity;
@Column('uuid', { nullable: true })
jobId: string;
+ @Index('IDX_USER_JOB', ['user', 'jobId'], { unique: true })
@CreateDateColumn()
savedAt: Date;
constructor(savedJob?: Partial<SavedJobsEntity>) {
this.id = savedJob?.id;
this.user = savedJob?.user;
this.jobId = savedJob?.jobId;
this.savedAt = savedJob?.savedAt;
}
}Não esqueça de adicionar a importação:
import { Column, CreateDateColumn, Entity, Index, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';🧰 Tools
🪛 ESLint
[error] 11-11: Delete ··
(prettier/prettier)
[error] 12-12: Delete ··
(prettier/prettier)
[error] 13-13: Replace ···· with ··
(prettier/prettier)
[error] 14-14: Delete ··
(prettier/prettier)
[error] 15-15: Delete ··
(prettier/prettier)
[error] 16-16: Delete ··
(prettier/prettier)
[error] 17-17: Delete ··
(prettier/prettier)
[error] 18-18: Delete ··
(prettier/prettier)
[error] 19-19: Replace ···· with ··
(prettier/prettier)
[error] 20-20: Delete ··
(prettier/prettier)
[error] 21-21: Delete ··
(prettier/prettier)
[error] 22-22: Delete ··
(prettier/prettier)
[error] 23-23: Delete ··
(prettier/prettier)
[error] 24-24: Delete ··
(prettier/prettier)
[error] 25-25: Delete ··
(prettier/prettier)
[error] 26-26: Delete ··
(prettier/prettier)
[error] 27-27: Delete ··
(prettier/prettier)
[error] 28-28: Delete ··
(prettier/prettier)
[error] 29-29: Delete ··
(prettier/prettier)
[error] 30-30: Replace ······ with ····
(prettier/prettier)
[error] 31-31: Delete ··
(prettier/prettier)
[error] 32-32: Replace ······ with ····
(prettier/prettier)
[error] 33-33: Delete ··
(prettier/prettier)
[error] 34-34: Delete ··
(prettier/prettier)
[error] 35-35: Replace ··} with }⏎
(prettier/prettier)
| @Post() | ||
| @ApiBearerAuth() | ||
| @UseGuards(AuthGuard()) | ||
| @SwaggerCreateSavedJobs() | ||
| @ApiOperation({ summary: 'Salvar vaga para um usuário' }) | ||
| async saveJob( | ||
| @Body() createSavedJobDto: CreateSavedJobDto, | ||
| ): Promise<SavedJobsEntity> { | ||
| try { | ||
| console.log(createSavedJobDto); | ||
|
|
||
| return await this.savedJobsService.saveJob(createSavedJobDto); | ||
| } catch (error) { | ||
| throw new HttpException( | ||
| 'Erro ao salvar vaga', | ||
| HttpStatus.INTERNAL_SERVER_ERROR, | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Utilize o usuário autenticado no endpoint de salvar vagas.
O endpoint está protegido com AuthGuard(), mas não está utilizando as informações do usuário autenticado. Em vez de receber o userId no DTO, é mais seguro obter o usuário diretamente do token de autenticação.
@Post()
@ApiBearerAuth()
@UseGuards(AuthGuard())
@SwaggerCreateSavedJobs()
@ApiOperation({ summary: 'Salvar vaga para um usuário' })
async saveJob(
@Body() createSavedJobDto: CreateSavedJobDto,
+ @GetUser() user: UsersEntity,
): Promise<SavedJobsEntity> {
try {
- console.log(createSavedJobDto);
- return await this.savedJobsService.saveJob(createSavedJobDto);
+ return this.savedJobsService.saveJob({
+ ...createSavedJobDto,
+ userId: user.id
+ });
} catch (error) {
throw new HttpException(
'Erro ao salvar vaga',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}Não esqueça de adicionar as importações:
import { GetUser } from '../auth/get-user.decorator';
import { UsersEntity } from '../../database/entities/users.entity';E atualize o DTO para tornar o userId opcional:
export class CreateSavedJobDto {
- @IsUUID()
- @IsNotEmpty()
+ @IsOptional()
+ @IsUUID()
userId: string;
@IsUUID()
@IsNotEmpty()
jobId: string;
}🧰 Tools
🪛 ESLint
[error] 25-25: Delete ·
(prettier/prettier)
[error] 33-33: Delete ······
(prettier/prettier)
… tabelas. ajuste no controller de savedjobs na validacao ds 7 dias
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (6)
src/database/entities/savedjobs.entity.ts (1)
12-40: 🛠️ Refactor suggestionAdicione restrição de unicidade para evitar duplicações e ajuste a configuração da relação job.
A entidade SavedJobsEntity está bem estruturada, mas possui duas melhorias importantes a serem implementadas:
- Não há uma restrição de unicidade para evitar que um usuário salve a mesma vaga várias vezes
- A relação com a entidade JobsEntity não tem configuração de
onDeletepara tratar exclusões em cascataAplique estas alterações:
+import { Column, CreateDateColumn, Entity, + Index, JoinColumn, ManyToOne, PrimaryGeneratedColumn, } from 'typeorm'; import { UsersEntity } from './users.entity'; import { JobsEntity } from './jobs.entity'; @Entity('tb_saved_jobs') +@Index('IDX_USER_JOB', ['user', 'job'], { unique: true }) export class SavedJobsEntity { @PrimaryGeneratedColumn('uuid') id: string; @ManyToOne(() => UsersEntity, (user) => user.savedJobs, { onDelete: 'CASCADE', nullable: false, }) @JoinColumn({ name: 'userId' }) user: UsersEntity; - @ManyToOne(() => JobsEntity, (job) => job.savedJobs, { eager: false }) + @ManyToOne(() => JobsEntity, (job) => job.savedJobs, { + eager: false, + onDelete: 'CASCADE', + nullable: false + }) + @JoinColumn({ name: 'jobId' }) job: JobsEntity; @CreateDateColumn() savedAt: Date;Além disso, o nome da coluna da relação deve ser consistente com o uso em migrações (
jobId).src/modules/savedjobs/services/savedjobs.service.ts (1)
42-51: 🛠️ Refactor suggestionVerificação de duplicidade implementada, mas falta proteção contra condições de corrida.
A verificação se o usuário já salvou a vaga está implementada corretamente, mas ainda pode haver problemas de duplicidade em cenários concorrentes (por exemplo, se duas requisições simultâneas passarem pela checagem antes de salvar).
Recomendo adicionar uma constraint de unicidade no banco de dados para o par
(userId, jobId)em uma migração separada:// Exemplo de migração para adicionar constraint de unicidade await queryRunner.query( `ALTER TABLE "tb_saved_jobs" ADD CONSTRAINT "UQ_user_job" UNIQUE ("userId", "jobId")` );🧰 Tools
🪛 ESLint
[error] 45-45: Replace
}·,·with·},(prettier/prettier)
[error] 50-50: Replace
'Este·trabalho·já·foi·salvo·por·este·usuário.'with⏎········'Este·trabalho·já·foi·salvo·por·este·usuário.',⏎······(prettier/prettier)
src/modules/savedjobs/savedjobs.controller.ts (4)
49-53: Especifique um tipo de retorno para o método getAllSavedJobs.O método não tem um tipo de retorno explícito, o que dificulta a compreensão da API.
async getAllSavedJobs( @Query() pageOptionsDto: PageOptionsDto, @Query() query: GetAllSavedJobsDto, - ) { + ): Promise<PageDto<SavedJobsEntity>> {Não esqueça de adicionar a importação:
import { PageDto } from 'src/shared/pagination';🧰 Tools
🪛 ESLint
[error] 49-49: Replace
·summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.'with⏎····summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.',⏎·(prettier/prettier)
25-27: 🛠️ Refactor suggestionUtilize o usuário autenticado ao invés de receber userId via DTO.
O endpoint utiliza autenticação, mas ainda recebe o userId via DTO ao invés de extraí-lo do token JWT, o que pode permitir que um usuário salve vagas em nome de outro.
async saveJob( @Body() createSavedJobDto: CreateSavedJobDto, + @GetUser() user: UsersEntity, ): Promise<any> { try { - const savedJob = await this.savedJobsService.saveJob(createSavedJobDto); + const savedJob = await this.savedJobsService.saveJob({ + ...createSavedJobDto, + userId: user.id + });Não esqueça de adicionar as importações:
import { GetUser } from '../../decorators/get-user.decorator'; import { UsersEntity } from '../../../database/entities/users.entity';🧰 Tools
🪛 ESLint
[error] 25-27: Replace
⏎····@Body()·createSavedJobDto:·CreateSavedJobDto,⏎··):·Promise<any>·{·with@Body()·createSavedJobDto:·CreateSavedJobDto):·Promise<any>·{(prettier/prettier)
54-61: 🛠️ Refactor suggestionInclua detalhes do erro original na exceção.
A exceção lançada não inclui informações sobre o erro original no método
getAllSavedJobs, ao contrário do que foi feito no métodosaveJob.} catch (error) { throw new HttpException( - 'Erro ao buscar vagas salvas', + `Erro ao buscar vagas salvas: ${error.message || 'erro desconhecido'}`, HttpStatus.INTERNAL_SERVER_ERROR, ); }
45-53: 🛠️ Refactor suggestionUtilize o usuário autenticado para filtrar os resultados.
O endpoint utiliza autenticação, mas não está aproveitando as informações do usuário para filtrar apenas suas próprias vagas salvas.
async getAllSavedJobs( @Query() pageOptionsDto: PageOptionsDto, @Query() query: GetAllSavedJobsDto, + @GetUser() user: UsersEntity, ) { try { - return await this.findAllSavedJobsService.execute(pageOptionsDto, query); + return await this.findAllSavedJobsService.execute(pageOptionsDto, { + ...query, + userId: user.id + });🧰 Tools
🪛 ESLint
[error] 49-49: Replace
·summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.'with⏎····summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.',⏎·(prettier/prettier)
🧹 Nitpick comments (8)
src/modules/auth/services/auth-login.service.ts (1)
21-21: Remover linha em branco desnecessáriaA linha em branco inserida em 21 gera um desvio nas regras do Prettier (prettier/prettier). Recomendo removê-la para manter o estilo consistente.
🧰 Tools
🪛 ESLint
[error] 21-22: Delete
⏎(prettier/prettier)
src/database/migrations/1745267234389-migrations.ts (1)
1-14: Migração corretamente implementada para atualizar a coluna expiresAt.A migração remove o valor padrão da coluna "expiresAt" na tabela "tb_saved_jobs", o que é adequado se a intenção é que este valor seja definido explicitamente durante a criação dos registros.
No entanto, há vários problemas de formatação apontados pelo ESLint que precisam ser corrigidos para manter a consistência do código.
Sugiro aplicar as seguintes alterações de formatação:
-import { MigrationInterface, QueryRunner } from "typeorm"; +import { MigrationInterface, QueryRunner } from 'typeorm'; export class Migrations1745267234389 implements MigrationInterface { - name = 'Migrations1745267234389' + name = 'Migrations1745267234389'; public async up(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(`ALTER TABLE "tb_saved_jobs" ALTER COLUMN "expiresAt" DROP DEFAULT`); + await queryRunner.query( + `ALTER TABLE "tb_saved_jobs" ALTER COLUMN "expiresAt" DROP DEFAULT` + ); } public async down(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(`ALTER TABLE "tb_saved_jobs" ALTER COLUMN "expiresAt" SET DEFAULT now()`); + await queryRunner.query( + `ALTER TABLE "tb_saved_jobs" ALTER COLUMN "expiresAt" SET DEFAULT now()` + ); } }🧰 Tools
🪛 ESLint
[error] 1-1: Replace
"typeorm"with'typeorm'(prettier/prettier)
[error] 4-4: Replace
··name·=·'Migrations1745267234389'withname·=·'Migrations1745267234389';(prettier/prettier)
[error] 6-6: Delete
··(prettier/prettier)
[error] 7-7: Replace
········await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"expiresAt"·DROP·DEFAULT);with····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"expiresAt"·DROP·DEFAULT,(prettier/prettier)
[error] 8-9: Replace
}⏎with);(prettier/prettier)
[error] 10-10: Insert
}⏎⏎(prettier/prettier)
[error] 11-11: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"expiresAt"·SET·DEFAULT·now()`` withawait·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"expiresAt"·SET·DEFAULT·now()`,⏎····`(prettier/prettier)
[error] 12-13: Replace
··}⏎with}(prettier/prettier)
src/database/entities/savedjobs.entity.ts (1)
30-31: Configure propriedades adicionais para a coluna expiresAt.A coluna expiresAt precisa de configurações adicionais como nullable e default para garantir comportamento consistente.
- @Column({ type: 'timestamp' }) + @Column({ type: 'timestamp', nullable: true }) expiresAt: Date;src/database/migrations/1745262789787-migrations.ts (2)
6-17: Utilize uma abordagem mais segura para verificação e adição de colunas.A abordagem de verificar a existência da coluna antes de adicioná-la é boa, mas a implementação pode ser melhorada.
- As alterações da tabela poderiam ser agrupadas em uma transação para garantir consistência.
- Adicionar verificação de existência das outras operações para evitar erros.
public async up(queryRunner: QueryRunner): Promise<void> { const hasExpiresAt = await queryRunner.hasColumn('tb_saved_jobs', 'expiresAt'); if (!hasExpiresAt) { await queryRunner.query(`ALTER TABLE "tb_saved_jobs" ADD "expiresAt" TIMESTAMP NOT NULL DEFAULT now()`); } + // Verificar se a constraint existe antes de removê-la + const tableConstraints = await queryRunner.query( + `SELECT * FROM information_schema.table_constraints + WHERE table_name = 'tb_saved_jobs' AND constraint_name = 'FK_3845b06bfda63ccc1da359b378a'` + ); + + if (tableConstraints.length > 0) { await queryRunner.query(`ALTER TABLE "tb_saved_jobs" DROP CONSTRAINT "FK_3845b06bfda63ccc1da359b378a"`); + }Além disso, há vários problemas de formatação apontados pelo ESLint que deveriam ser corrigidos.
🧰 Tools
🪛 ESLint
[error] 6-6: Delete
··(prettier/prettier)
[error] 7-7: Replace
········const·hasExpiresAt·=·await·queryRunner.hasColumn('tb_saved_jobs',·'expiresAt');with····const·hasExpiresAt·=·await·queryRunner.hasColumn(⏎······'tb_saved_jobs',⏎······'expiresAt',(prettier/prettier)
[error] 8-8: Insert
);⏎(prettier/prettier)
[error] 9-9: Replace
············await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now());with······await·queryRunner.query(⏎········ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now(),(prettier/prettier)
[error] 10-11: Replace
··}⏎with);(prettier/prettier)
[error] 12-12: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a");with}⏎⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a",(prettier/prettier)
[error] 13-13: Insert
);⏎(prettier/prettier)
[error] 14-14: Delete
····(prettier/prettier)
[error] 15-15: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL);withawait·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL,(prettier/prettier)
[error] 16-16: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION);with);⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION,(prettier/prettier)
19-27: Garanta que o método down seja totalmente compatível com as alterações do método up.O método down deve reverter exatamente as alterações feitas no método up, na ordem inversa, para garantir consistência.
Sugiro adicionar verificações similares às do método up para evitar erros durante a reversão:
public async down(queryRunner: QueryRunner): Promise<void> { + // Verificar se a constraint existe antes de removê-la + const tableConstraints = await queryRunner.query( + `SELECT * FROM information_schema.table_constraints + WHERE table_name = 'tb_saved_jobs' AND constraint_name = 'FK_3845b06bfda63ccc1da359b378a'` + ); + + if (tableConstraints.length > 0) { await queryRunner.query(`ALTER TABLE "tb_saved_jobs" DROP CONSTRAINT "FK_3845b06bfda63ccc1da359b378a"`); + }Adicionalmente, resolva os problemas de formatação apontados pelo ESLint.
🧰 Tools
🪛 ESLint
[error] 19-19: Insert
}⏎⏎(prettier/prettier)
[error] 20-20: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a");withawait·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a",(prettier/prettier)
[error] 21-21: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·DROP·NOT·NULL);with);⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·DROP·NOT·NULL,(prettier/prettier)
[error] 22-22: Insert
);⏎(prettier/prettier)
[error] 23-23: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"jobId"·character·varying·NOT·NULL);withawait·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·"jobId"·character·varying·NOT·NULL,(prettier/prettier)
[error] 24-24: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·COLUMN·"expiresAt");with);⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·COLUMN·"expiresAt",(prettier/prettier)
[error] 25-25: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION);with);⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION,(prettier/prettier)
[error] 26-26: Replace
··with····);⏎(prettier/prettier)
src/database/migrations/1745261990419-migrations.ts (1)
6-13:Details
❓ Verification inconclusive
Adicionada a funcionalidade de expiração para vagas salvas com CASCADE na exclusão de usuários.
Esta migração adiciona uma coluna
expiresAte muda a restrição de chave estrangeira para CASCADE na exclusão. Isto significa que quando um usuário for excluído, todas as suas vagas salvas serão automaticamente removidas.A alteração do tipo da coluna
jobIdde character varying para UUID pode causar problemas de compatibilidade se já existirem dados. Certifique-se de que todos os jobIds existentes são compatíveis com o formato UUID.Execute este script para verificar se todos os valores de
jobIdexistentes são compatíveis com o formato UUID:
🏁 Script executed:
#!/bin/bash # Verifica se os valores existentes de jobId são compatíveis com UUID echo "Verificando formato de jobId na tabela tb_saved_jobs..." rg -A 3 "INSERT INTO.*tb_saved_jobs.*jobId" --include "*.ts" --include "*.sql"Length of output: 643
Para ajustar o comando e buscar ocorrências de INSERT em
tb_saved_jobs, execute:
🏁 Script executed:
#!/bin/bash echo "Buscando INSERT INTO tb_saved_jobs em arquivos .ts e .sql..." rg -g '*.ts' -g '*.sql' 'INSERT INTO.*tb_saved_jobs' -A3Length of output: 189
Verifique compatibilidade de jobId existente com UUID antes de aplicar a migração
Não foram encontradas inserções de
tb_saved_jobsno código; é necessário confirmar diretamente no banco de dados que todos os valores atuais dejobIdseguem o formato UUID.Sugestão de consulta SQL para validação:
SELECT jobId FROM tb_saved_jobs WHERE jobId IS NOT NULL AND jobId !~* '^[0-9A-F0-9]{8}-[0-9A-F0-9]{4}-[0-9A-F0-9]{4}-[0-9A-F0-9]{4}-[0-9A-F0-9]{12}$';Se essa consulta retornar qualquer registro, será preciso converter ou atualizar esses valores antes de executar a migração.
🧰 Tools
🪛 ESLint
[error] 6-6: Delete
··(prettier/prettier)
[error] 7-7: Replace
········await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a");with····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a",(prettier/prettier)
[error] 8-8: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now());with);⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now(),(prettier/prettier)
[error] 9-9: Insert
);⏎(prettier/prettier)
[error] 10-10: Delete
····(prettier/prettier)
[error] 11-11: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL);withawait·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL,(prettier/prettier)
[error] 12-12: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION);with);⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION,(prettier/prettier)
src/modules/savedjobs/services/savedjobs.service.ts (1)
66-69: Considere limitar os dados retornados nas relações.Ao retornar as relações completas
userejob, você pode estar expondo mais informações do que o necessário, incluindo dados sensíveis do usuário. Considere selecionar apenas os campos necessários dessas entidades.return this.savedJobsRepository.findOne({ where: { id: savedJob.id }, - relations: ['user', 'job'], + relations: ['user', 'job'], + select: { + id: true, + savedAt: true, + expiresAt: true, + user: { + id: true, + name: true, + email: true + }, + job: { + id: true, + title: true, + company: true + } + } });src/modules/savedjobs/savedjobs.controller.ts (1)
27-27: Especifique um tipo de retorno mais preciso.O tipo de retorno
Promise<any>é muito genérico. Especifique um tipo mais preciso para melhorar a documentação da API.- ): Promise<any> { + ): Promise<{ message: string; statusCode: number; savedJob: SavedJobsEntity }> {🧰 Tools
🪛 ESLint
[error] 25-27: Replace
⏎····@Body()·createSavedJobDto:·CreateSavedJobDto,⏎··):·Promise<any>·{·with@Body()·createSavedJobDto:·CreateSavedJobDto):·Promise<any>·{(prettier/prettier)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/database/entities/jobs.entity.ts(2 hunks)src/database/entities/savedjobs.entity.ts(1 hunks)src/database/migrations/1745261990419-migrations.ts(1 hunks)src/database/migrations/1745262789787-migrations.ts(1 hunks)src/database/migrations/1745267234389-migrations.ts(1 hunks)src/modules/auth/services/auth-login.service.ts(1 hunks)src/modules/company/repository/company-repository.ts(0 hunks)src/modules/savedjobs/savedjobs.controller.ts(1 hunks)src/modules/savedjobs/services/savedjobs.service.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/modules/company/repository/company-repository.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/database/entities/savedjobs.entity.ts (2)
src/database/entities/jobs.entity.ts (1)
Entity(20-142)src/database/entities/users.entity.ts (1)
Entity(22-108)
🪛 ESLint
src/database/migrations/1745267234389-migrations.ts
[error] 1-1: Replace "typeorm" with 'typeorm'
(prettier/prettier)
[error] 4-4: Replace ··name·=·'Migrations1745267234389' with name·=·'Migrations1745267234389';
(prettier/prettier)
[error] 6-6: Delete ··
(prettier/prettier)
[error] 7-7: Replace ········await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"expiresAt"·DROP·DEFAULT); with ····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"expiresAt"·DROP·DEFAULT,
(prettier/prettier)
[error] 8-9: Replace }⏎ with );
(prettier/prettier)
[error] 10-10: Insert }⏎⏎
(prettier/prettier)
[error] 11-11: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"expiresAt"·SET·DEFAULT·now()`` with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"expiresAt"·SET·DEFAULT·now()`,⏎····`
(prettier/prettier)
[error] 12-13: Replace ··}⏎ with }
(prettier/prettier)
src/database/entities/jobs.entity.ts
[error] 141-141: Delete ··
(prettier/prettier)
src/modules/savedjobs/services/savedjobs.service.ts
[error] 24-24: Replace createSavedJobDto:·CreateSavedJobDto with ⏎····createSavedJobDto:·CreateSavedJobDto,⏎··
(prettier/prettier)
[error] 26-26: Delete ·
(prettier/prettier)
[error] 28-28: Replace 'O·ID·do·usuário·e·o·ID·da·vaga·devem·ser·fornecidos' with ⏎········'O·ID·do·usuário·e·o·ID·da·vaga·devem·ser·fornecidos',⏎······
(prettier/prettier)
[error] 30-30: Delete ··
(prettier/prettier)
[error] 35-35: Delete ··
(prettier/prettier)
[error] 41-41: Delete ··
(prettier/prettier)
[error] 45-45: Replace }·,· with ·},
(prettier/prettier)
[error] 50-50: Replace 'Este·trabalho·já·foi·salvo·por·este·usuário.' with ⏎········'Este·trabalho·já·foi·salvo·por·este·usuário.',⏎······
(prettier/prettier)
[error] 52-52: Delete ··
(prettier/prettier)
[error] 59-59: Delete ·
(prettier/prettier)
src/database/migrations/1745261990419-migrations.ts
[error] 1-1: Replace "typeorm" with 'typeorm'
(prettier/prettier)
[error] 4-4: Replace ····name·=·'Migrations1745261990419' with ··name·=·'Migrations1745261990419';
(prettier/prettier)
[error] 6-6: Delete ··
(prettier/prettier)
[error] 7-7: Replace ········await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"); with ····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a",
(prettier/prettier)
[error] 8-8: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now()); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now(),
(prettier/prettier)
[error] 9-9: Insert );⏎
(prettier/prettier)
[error] 10-10: Delete ····
(prettier/prettier)
[error] 11-11: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL,
(prettier/prettier)
[error] 12-12: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION,
(prettier/prettier)
[error] 13-14: Replace }⏎ with );
(prettier/prettier)
[error] 15-15: Insert }⏎⏎
(prettier/prettier)
[error] 16-16: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a",
(prettier/prettier)
[error] 17-17: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·DROP·NOT·NULL); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·DROP·NOT·NULL,
(prettier/prettier)
[error] 18-18: Insert );⏎
(prettier/prettier)
[error] 19-19: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"jobId"·character·varying·NOT·NULL); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·"jobId"·character·varying·NOT·NULL,
(prettier/prettier)
[error] 20-20: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·COLUMN·"expiresAt"); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·COLUMN·"expiresAt",
(prettier/prettier)
[error] 21-21: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION`` with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION`,⏎····`
(prettier/prettier)
[error] 22-23: Replace ··}⏎ with }
(prettier/prettier)
src/database/migrations/1745262789787-migrations.ts
[error] 1-1: Replace "typeorm" with 'typeorm'
(prettier/prettier)
[error] 4-4: Replace ····name·=·'Migrations1745262789787' with ··name·=·'Migrations1745262789787';
(prettier/prettier)
[error] 6-6: Delete ··
(prettier/prettier)
[error] 7-7: Replace ········const·hasExpiresAt·=·await·queryRunner.hasColumn('tb_saved_jobs',·'expiresAt'); with ····const·hasExpiresAt·=·await·queryRunner.hasColumn(⏎······'tb_saved_jobs',⏎······'expiresAt',
(prettier/prettier)
[error] 8-8: Insert );⏎
(prettier/prettier)
[error] 9-9: Replace ············await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now()); with ······await·queryRunner.query(⏎········ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now(),
(prettier/prettier)
[error] 10-11: Replace ··}⏎ with );
(prettier/prettier)
[error] 12-12: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"); with }⏎⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a",
(prettier/prettier)
[error] 13-13: Insert );⏎
(prettier/prettier)
[error] 14-14: Delete ····
(prettier/prettier)
[error] 15-15: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL,
(prettier/prettier)
[error] 16-16: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION,
(prettier/prettier)
[error] 17-18: Replace }⏎ with );
(prettier/prettier)
[error] 19-19: Insert }⏎⏎
(prettier/prettier)
[error] 20-20: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a",
(prettier/prettier)
[error] 21-21: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·DROP·NOT·NULL); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·DROP·NOT·NULL,
(prettier/prettier)
[error] 22-22: Insert );⏎
(prettier/prettier)
[error] 23-23: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"jobId"·character·varying·NOT·NULL); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·"jobId"·character·varying·NOT·NULL,
(prettier/prettier)
[error] 24-24: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·COLUMN·"expiresAt"); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·COLUMN·"expiresAt",
(prettier/prettier)
[error] 25-25: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION,
(prettier/prettier)
[error] 26-26: Replace ·· with ····);⏎
(prettier/prettier)
src/modules/auth/services/auth-login.service.ts
[error] 21-22: Delete ⏎
(prettier/prettier)
src/modules/savedjobs/savedjobs.controller.ts
[error] 1-1: Replace ·Body,·Controller,·Get,·HttpException,·HttpStatus,·Post,·Query,·UseGuards· with ⏎··Body,⏎··Controller,⏎··Get,⏎··HttpException,⏎··HttpStatus,⏎··Post,⏎··Query,⏎··UseGuards,⏎
(prettier/prettier)
[error] 25-27: Replace ⏎····@Body()·createSavedJobDto:·CreateSavedJobDto,⏎··):·Promise<any>·{· with @Body()·createSavedJobDto:·CreateSavedJobDto):·Promise<any>·{
(prettier/prettier)
[error] 37-38: Delete ⏎······
(prettier/prettier)
[error] 49-49: Replace ·summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.' with ⏎····summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.',⏎·
(prettier/prettier)
🔇 Additional comments (3)
src/database/entities/jobs.entity.ts (1)
18-18: Importação corretamente adicionada.A importação da entidade SavedJobsEntity foi adicionada corretamente para estabelecer o relacionamento com Jobs.
src/database/entities/savedjobs.entity.ts (1)
1-10: Importações corretamente configuradas.As importações necessárias do TypeORM e as entidades relacionadas (UsersEntity e JobsEntity) foram configuradas adequadamente.
src/modules/savedjobs/services/savedjobs.service.ts (1)
53-55: Bom controle de expiração implementado.A implementação do controle de expiração das vagas salvas está correta, definindo uma data de expiração 7 dias após a data de salvamento. Isto está alinhado com a inclusão da coluna
expiresAtna tabela.
MikaelMelo1
left a comment
There was a problem hiding this comment.
Algumas observações.
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/modules/savedjobs/services/find-all-savedjobs.service.ts (1)
18-18:⚠️ Potential issueCorrigir mensagem de erro para refletir a operação sendo realizada.
A mensagem de erro atual menciona "Falha ao salvar os trabalhos salvos", mas esta função é para buscar/listar os jobs salvos, não para salvá-los.
- throw new InternalServerErrorException(`Falha ao salvar os trabalhos salvos: ${error.message}`); + throw new InternalServerErrorException(`Falha ao buscar os jobs salvos: ${error.message}`);🧰 Tools
🪛 ESLint
[error] 18-18: Replace ``Falha·ao·salvar·os·trabalhos·salvos:·${error.message}
);␍with `⏎········`Falha·ao·salvar·os·trabalhos·salvos:·${error.message}`,⏎······);`(prettier/prettier)
🧹 Nitpick comments (2)
src/modules/savedjobs/services/find-all-savedjobs.service.ts (2)
1-5: Padronizar uso de aspas simples nos imports.Os imports estão usando aspas duplas, mas o padrão do projeto parece ser usar aspas simples conforme indicado pela análise estática.
- import { Injectable, InternalServerErrorException } from "@nestjs/common"; - import { SavedJobsEntity } from "src/database/entities/savedjobs.entity"; - import { PageDto, PageOptionsDto } from "src/shared/pagination"; - import { SavedJobsRepository } from "../repository/savedjobs.repository"; - import { GetAllSavedJobsDto } from "../dtos/get-all-savedjobs.dto"; + import { Injectable, InternalServerErrorException } from '@nestjs/common'; + import { SavedJobsEntity } from 'src/database/entities/savedjobs.entity'; + import { PageDto, PageOptionsDto } from 'src/shared/pagination'; + import { SavedJobsRepository } from '../repository/savedjobs.repository'; + import { GetAllSavedJobsDto } from '../dtos/get-all-savedjobs.dto';🧰 Tools
🪛 ESLint
[error] 1-1: Replace
"@nestjs/common";␍with'@nestjs/common';(prettier/prettier)
[error] 2-2: Replace
"src/database/entities/savedjobs.entity";␍with'src/database/entities/savedjobs.entity';(prettier/prettier)
[error] 3-3: Replace
"src/shared/pagination";␍with'src/shared/pagination';(prettier/prettier)
[error] 4-4: Replace
"../repository/savedjobs.repository";␍with'../repository/savedjobs.repository';(prettier/prettier)
[error] 5-5: Replace
"../dtos/get-all-savedjobs.dto";␍with'../dtos/get-all-savedjobs.dto';(prettier/prettier)
16-16: Formatar parâmetros em várias linhas para melhor legibilidade.Para manter consistência com o estilo do projeto, considere formatar os parâmetros em múltiplas linhas.
- return await this.savedJobsRepository.getAllSavedJobs(pageOptionsDto, filters); + return await this.savedJobsRepository.getAllSavedJobs( + pageOptionsDto, + filters, + );🧰 Tools
🪛 ESLint
[error] 16-16: Replace
pageOptionsDto,·filters);␍with⏎········pageOptionsDto,⏎········filters,⏎······);(prettier/prettier)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/modules/savedjobs/services/find-all-savedjobs.service.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/modules/savedjobs/services/find-all-savedjobs.service.ts (4)
src/modules/savedjobs/repository/savedjobs.repository.ts (1)
Injectable(8-44)src/shared/pagination/pageOptions.dto.ts (1)
PageOptionsDto(6-42)src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts (1)
GetAllSavedJobsDto(4-22)src/shared/pagination/page.dto.ts (1)
PageDto(4-14)
🪛 ESLint
src/modules/savedjobs/services/find-all-savedjobs.service.ts
[error] 1-1: Replace "@nestjs/common";␍ with '@nestjs/common';
(prettier/prettier)
[error] 2-2: Replace "src/database/entities/savedjobs.entity";␍ with 'src/database/entities/savedjobs.entity';
(prettier/prettier)
[error] 3-3: Replace "src/shared/pagination";␍ with 'src/shared/pagination';
(prettier/prettier)
[error] 4-4: Replace "../repository/savedjobs.repository";␍ with '../repository/savedjobs.repository';
(prettier/prettier)
[error] 5-5: Replace "../dtos/get-all-savedjobs.dto";␍ with '../dtos/get-all-savedjobs.dto';
(prettier/prettier)
[error] 6-6: Delete ␍
(prettier/prettier)
[error] 7-7: Delete ␍
(prettier/prettier)
[error] 8-8: Delete ␍
(prettier/prettier)
[error] 9-9: Delete ␍
(prettier/prettier)
[error] 10-10: Delete ␍
(prettier/prettier)
[error] 11-11: Delete ␍
(prettier/prettier)
[error] 12-12: Delete ␍
(prettier/prettier)
[error] 13-13: Delete ␍
(prettier/prettier)
[error] 14-14: Delete ␍
(prettier/prettier)
[error] 15-15: Delete ␍
(prettier/prettier)
[error] 16-16: Replace pageOptionsDto,·filters);␍ with ⏎········pageOptionsDto,⏎········filters,⏎······);
(prettier/prettier)
[error] 17-17: Delete ␍
(prettier/prettier)
[error] 18-18: Replace ``Falha·ao·salvar·os·trabalhos·salvos:·${error.message});␍ with `⏎········`Falha·ao·salvar·os·trabalhos·salvos:·${error.message}`,⏎······);`
(prettier/prettier)
[error] 19-19: Delete ␍
(prettier/prettier)
[error] 20-20: Delete ␍
(prettier/prettier)
[error] 21-21: Insert ⏎
(prettier/prettier)
🔇 Additional comments (1)
src/modules/savedjobs/services/find-all-savedjobs.service.ts (1)
1-21: Arquitetura bem implementada para listar jobs salvos com paginação e filtros.O serviço está bem estruturado seguindo os padrões do NestJS com injeção de dependência, tratamento de erros e tipos bem definidos. A implementação utiliza corretamente os DTOs de paginação e filtros.
🧰 Tools
🪛 ESLint
[error] 1-1: Replace
"@nestjs/common";␍with'@nestjs/common';(prettier/prettier)
[error] 2-2: Replace
"src/database/entities/savedjobs.entity";␍with'src/database/entities/savedjobs.entity';(prettier/prettier)
[error] 3-3: Replace
"src/shared/pagination";␍with'src/shared/pagination';(prettier/prettier)
[error] 4-4: Replace
"../repository/savedjobs.repository";␍with'../repository/savedjobs.repository';(prettier/prettier)
[error] 5-5: Replace
"../dtos/get-all-savedjobs.dto";␍with'../dtos/get-all-savedjobs.dto';(prettier/prettier)
[error] 6-6: Delete
␍(prettier/prettier)
[error] 7-7: Delete
␍(prettier/prettier)
[error] 8-8: Delete
␍(prettier/prettier)
[error] 9-9: Delete
␍(prettier/prettier)
[error] 10-10: Delete
␍(prettier/prettier)
[error] 11-11: Delete
␍(prettier/prettier)
[error] 12-12: Delete
␍(prettier/prettier)
[error] 13-13: Delete
␍(prettier/prettier)
[error] 14-14: Delete
␍(prettier/prettier)
[error] 15-15: Delete
␍(prettier/prettier)
[error] 16-16: Replace
pageOptionsDto,·filters);␍with⏎········pageOptionsDto,⏎········filters,⏎······);(prettier/prettier)
[error] 17-17: Delete
␍(prettier/prettier)
[error] 18-18: Replace ``Falha·ao·salvar·os·trabalhos·salvos:·${error.message}
);␍with `⏎········`Falha·ao·salvar·os·trabalhos·salvos:·${error.message}`,⏎······);`(prettier/prettier)
[error] 19-19: Delete
␍(prettier/prettier)
[error] 20-20: Delete
␍(prettier/prettier)
[error] 21-21: Insert
⏎(prettier/prettier)
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts (1)
8-36: Decorador de Swagger implementado corretamente com todas as respostas necessárias.O decorador implementa corretamente todas as possíveis respostas para a operação de salvar uma vaga:
- Sucesso (201): Retorna a entidade
SavedJobsEntity- Requisição inválida (400): Usa o tipo correto
BadRequestSwagger- Conflito (409): Usa o novo tipo
ConflictSwaggerpara indicar que a vaga já foi salva- Não encontrado (404): Usa o tipo correto
NotFoundSwaggerAs correções solicitadas nos comentários anteriores foram implementadas corretamente, como o uso de
SavedJobsEntitypara o tipo de resposta de sucesso e o uso deConflictSwaggerpara o tipo de resposta de conflito.No entanto, há alguns problemas de formatação que precisam ser corrigidos para seguir o padrão de estilo do projeto. Recomendo usar uma ferramenta de formatação automática como o Prettier.
Além disso, sugiro considerar a adição de uma descrição mais detalhada na documentação para ajudar outros desenvolvedores:
ApiOperation({ summary: 'Salvar uma vaga', - description: 'Cria um novo registro de vaga salva para o usuário autenticado. Requer dados obrigatórios no corpo da requisição.', + description: 'Cria um novo registro de vaga salva para o usuário autenticado. Requer dados obrigatórios no corpo da requisição, incluindo o ID da vaga a ser salva.', }),🧰 Tools
🪛 ESLint
[error] 8-8: Delete
␍(prettier/prettier)
[error] 9-9: Delete
␍(prettier/prettier)
[error] 10-10: Delete
␍(prettier/prettier)
[error] 11-11: Delete
␍(prettier/prettier)
[error] 12-12: Delete
␍(prettier/prettier)
[error] 13-13: Replace
·'Cria·um·novo·registro·de·vaga·salva·para·o·usuário·autenticado.·Requer·dados·obrigatórios·no·corpo·da·requisição.',␍with⏎········'Cria·um·novo·registro·de·vaga·salva·para·o·usuário·autenticado.·Requer·dados·obrigatórios·no·corpo·da·requisição.',(prettier/prettier)
[error] 14-14: Delete
␍(prettier/prettier)
[error] 15-15: Delete
␍(prettier/prettier)
[error] 16-16: Delete
␍(prettier/prettier)
[error] 17-17: Replace
·'Vaga·salva·com·sucesso.·Retorna·os·dados·do·registro·criado.',␍with⏎········'Vaga·salva·com·sucesso.·Retorna·os·dados·do·registro·criado.',(prettier/prettier)
[error] 18-18: Delete
␍(prettier/prettier)
[error] 19-19: Delete
␍(prettier/prettier)
[error] 20-20: Delete
␍(prettier/prettier)
[error] 21-21: Delete
␍(prettier/prettier)
[error] 22-22: Replace
·'Requisição·inválida.·Verifique·os·campos·enviados·no·corpo·da·requisição.',␍with⏎········'Requisição·inválida.·Verifique·os·campos·enviados·no·corpo·da·requisição.',(prettier/prettier)
[error] 23-23: Delete
␍(prettier/prettier)
[error] 24-24: Delete
␍(prettier/prettier)
[error] 25-25: Delete
␍(prettier/prettier)
[error] 26-26: Delete
␍(prettier/prettier)
[error] 27-27: Delete
␍(prettier/prettier)
[error] 28-28: Delete
␍(prettier/prettier)
[error] 29-29: Delete
␍(prettier/prettier)
[error] 30-30: Delete
␍(prettier/prettier)
[error] 31-31: Delete
␍(prettier/prettier)
[error] 32-32: Delete
␍(prettier/prettier)
[error] 33-33: Delete
␍(prettier/prettier)
[error] 34-34: Delete
␍(prettier/prettier)
[error] 35-35: Delete
␍(prettier/prettier)
[error] 36-36: Insert
⏎(prettier/prettier)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/shared/Swagger/conflict.swagger.ts(1 hunks)src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts (3)
src/shared/Swagger/bad-request.swagger.ts (1)
BadRequestSwagger(2-5)src/shared/Swagger/conflict.swagger.ts (1)
ConflictSwagger(3-6)src/shared/Swagger/not-found.swagger.ts (1)
NotFoundSwagger(2-5)
🪛 ESLint
src/shared/Swagger/conflict.swagger.ts
[error] 1-1: Replace "@nestjs/swagger";␍ with '@nestjs/swagger';
(prettier/prettier)
[error] 2-2: Delete ␍
(prettier/prettier)
[error] 3-3: Delete ␍
(prettier/prettier)
[error] 4-4: Replace ····@ApiProperty()␍ with ··@ApiProperty()
(prettier/prettier)
[error] 5-5: Replace ····message:·string;␍ with ··message:·string;
(prettier/prettier)
[error] 6-6: Replace ··} with }⏎
(prettier/prettier)
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts
[error] 1-1: Replace "@nestjs/common";␍ with '@nestjs/common';
(prettier/prettier)
[error] 2-2: Replace "@nestjs/swagger";␍ with '@nestjs/swagger';
(prettier/prettier)
[error] 3-3: Replace "../../not-found.swagger";␍ with '../../not-found.swagger';
(prettier/prettier)
[error] 4-4: Replace "../../bad-request.swagger";␍ with '../../bad-request.swagger';
(prettier/prettier)
[error] 5-5: Replace "src/database/entities/savedjobs.entity";␍ with 'src/database/entities/savedjobs.entity';
(prettier/prettier)
[error] 6-6: Replace "../../conflict.swagger";␍ with '../../conflict.swagger';
(prettier/prettier)
[error] 7-7: Delete ␍
(prettier/prettier)
[error] 8-8: Delete ␍
(prettier/prettier)
[error] 9-9: Delete ␍
(prettier/prettier)
[error] 10-10: Delete ␍
(prettier/prettier)
[error] 11-11: Delete ␍
(prettier/prettier)
[error] 12-12: Delete ␍
(prettier/prettier)
[error] 13-13: Replace ·'Cria·um·novo·registro·de·vaga·salva·para·o·usuário·autenticado.·Requer·dados·obrigatórios·no·corpo·da·requisição.',␍ with ⏎········'Cria·um·novo·registro·de·vaga·salva·para·o·usuário·autenticado.·Requer·dados·obrigatórios·no·corpo·da·requisição.',
(prettier/prettier)
[error] 14-14: Delete ␍
(prettier/prettier)
[error] 15-15: Delete ␍
(prettier/prettier)
[error] 16-16: Delete ␍
(prettier/prettier)
[error] 17-17: Replace ·'Vaga·salva·com·sucesso.·Retorna·os·dados·do·registro·criado.',␍ with ⏎········'Vaga·salva·com·sucesso.·Retorna·os·dados·do·registro·criado.',
(prettier/prettier)
[error] 18-18: Delete ␍
(prettier/prettier)
[error] 19-19: Delete ␍
(prettier/prettier)
[error] 20-20: Delete ␍
(prettier/prettier)
[error] 21-21: Delete ␍
(prettier/prettier)
[error] 22-22: Replace ·'Requisição·inválida.·Verifique·os·campos·enviados·no·corpo·da·requisição.',␍ with ⏎········'Requisição·inválida.·Verifique·os·campos·enviados·no·corpo·da·requisição.',
(prettier/prettier)
[error] 23-23: Delete ␍
(prettier/prettier)
[error] 24-24: Delete ␍
(prettier/prettier)
[error] 25-25: Delete ␍
(prettier/prettier)
[error] 26-26: Delete ␍
(prettier/prettier)
[error] 27-27: Delete ␍
(prettier/prettier)
[error] 28-28: Delete ␍
(prettier/prettier)
[error] 29-29: Delete ␍
(prettier/prettier)
[error] 30-30: Delete ␍
(prettier/prettier)
[error] 31-31: Delete ␍
(prettier/prettier)
[error] 32-32: Delete ␍
(prettier/prettier)
[error] 33-33: Delete ␍
(prettier/prettier)
[error] 34-34: Delete ␍
(prettier/prettier)
[error] 35-35: Delete ␍
(prettier/prettier)
[error] 36-36: Insert ⏎
(prettier/prettier)
🔇 Additional comments (2)
src/shared/Swagger/conflict.swagger.ts (1)
1-6: Classe de resposta para conflitos implementada corretamente.A classe
ConflictSwaggersegue o mesmo padrão que as outras classes de resposta de erro no projeto, comoNotFoundSwaggereBadRequestSwagger, mantendo a consistência do código.No entanto, há alguns problemas de formatação que precisam ser corrigidos para seguir o padrão de estilo do projeto:
-import { ApiProperty } from "@nestjs/swagger"; +import { ApiProperty } from '@nestjs/swagger'; export class ConflictSwagger { - @ApiProperty() - message: string; - } + @ApiProperty() + message: string; +}🧰 Tools
🪛 ESLint
[error] 1-1: Replace
"@nestjs/swagger";␍with'@nestjs/swagger';(prettier/prettier)
[error] 2-2: Delete
␍(prettier/prettier)
[error] 3-3: Delete
␍(prettier/prettier)
[error] 4-4: Replace
····@ApiProperty()␍with··@ApiProperty()(prettier/prettier)
[error] 5-5: Replace
····message:·string;␍with··message:·string;(prettier/prettier)
[error] 6-6: Replace
··}with}⏎(prettier/prettier)
src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts (1)
1-7: Importações corretas e completas.As importações estão corretamente definidas, incluindo a nova classe
ConflictSwaggerque foi adicionada.Há alguns problemas de formatação com aspas:
-import { applyDecorators, HttpStatus } from "@nestjs/common"; -import { ApiBearerAuth, ApiOperation, ApiResponse } from "@nestjs/swagger"; -import { NotFoundSwagger } from "../../not-found.swagger"; -import { BadRequestSwagger } from "../../bad-request.swagger"; -import { SavedJobsEntity } from "src/database/entities/savedjobs.entity"; -import { ConflictSwagger } from "../../conflict.swagger"; +import { applyDecorators, HttpStatus } from '@nestjs/common'; +import { ApiBearerAuth, ApiOperation, ApiResponse } from '@nestjs/swagger'; +import { NotFoundSwagger } from '../../not-found.swagger'; +import { BadRequestSwagger } from '../../bad-request.swagger'; +import { SavedJobsEntity } from 'src/database/entities/savedjobs.entity'; +import { ConflictSwagger } from '../../conflict.swagger';🧰 Tools
🪛 ESLint
[error] 1-1: Replace
"@nestjs/common";␍with'@nestjs/common';(prettier/prettier)
[error] 2-2: Replace
"@nestjs/swagger";␍with'@nestjs/swagger';(prettier/prettier)
[error] 3-3: Replace
"../../not-found.swagger";␍with'../../not-found.swagger';(prettier/prettier)
[error] 4-4: Replace
"../../bad-request.swagger";␍with'../../bad-request.swagger';(prettier/prettier)
[error] 5-5: Replace
"src/database/entities/savedjobs.entity";␍with'src/database/entities/savedjobs.entity';(prettier/prettier)
[error] 6-6: Replace
"../../conflict.swagger";␍with'../../conflict.swagger';(prettier/prettier)
[error] 7-7: Delete
␍(prettier/prettier)
There was a problem hiding this comment.
Actionable comments posted: 2
🔭 Outside diff range comments (1)
README.md (1)
355-355:⚠️ Potential issueCorrija o link quebrado na seção de Feedback.
O link na seção de Feedback aponta para uma âncora inexistente (#contribuição).
-Se você tiver algum feedback, por favor nos deixe saber por meio do nosso fazendo uma [contribuição](#contribuição). +Se você tiver algum feedback, por favor nos deixe saber por meio do nosso fazendo uma [contribuição](#como_contribuir).
♻️ Duplicate comments (1)
src/modules/savedjobs/savedjobs.controller.ts (1)
80-87: 🛠️ Refactor suggestionUtilize o usuário autenticado para filtrar as vagas salvas.
O endpoint está protegido com
AuthGuard(), mas não aproveita as informações do usuário autenticado para filtrar as vagas salvas.Utilize o decorator
@GetUser()para obter o usuário autenticado e filtre apenas as vagas salvas desse usuário:async getAllSavedJobs( @Query() pageOptionsDto: PageOptionsDto, @Query() query: GetAllSavedJobsDto, + @GetUser() user: UsersEntity, ): Promise<PageDto<SavedJobsEntity>> { try { - const savedJobs = await this.findAllSavedJobsService.getAllSavedJobs(pageOptionsDto, query); + const savedJobs = await this.findAllSavedJobsService.getAllSavedJobs( + pageOptionsDto, + { ...query, userId: user.id } + ); return savedJobs;Isso garante que um usuário só pode ver suas próprias vagas salvas, melhorando a segurança da API.
🧰 Tools
🪛 ESLint
[error] 85-85: Replace
pageOptionsDto,·querywith⏎········pageOptionsDto,⏎········query,⏎······(prettier/prettier)
🧹 Nitpick comments (4)
src/modules/savedjobs/savedjobs.controller.ts (1)
43-43: Defina um tipo de retorno específico para melhorar a tipagem.O método
saveJobestá usandoPromise<any>como tipo de retorno, o que dificulta a compreensão da API.Defina um tipo específico para melhorar a tipagem e documentação:
- ): Promise<any> { + ): Promise<{ + message: string; + statusCode: number; + savedJob: SavedJobsEntity; + }> {Não esqueça de adicionar o import caso necessário:
import { SavedJobsEntity } from '../../database/entities/savedjobs.entity';🧰 Tools
🪛 ESLint
[error] 41-43: Replace
⏎····@Body()·createSavedJobDto:·CreateSavedJobDto,⏎··with@Body()·createSavedJobDto:·CreateSavedJobDto(prettier/prettier)
README.md (3)
9-9: Adicione texto alternativo (alt text) à imagem do banner.A imagem do banner não tem um texto alternativo adequado, o que prejudica a acessibilidade.
-<img width="100%" src="https://capsule-render.vercel.app/api?type=waving&width=100%&color=2088f2&fontColor=ffffff&height=300§ion=header&text=Sou%20Junior&fontSize=90&animation=fadeIn&fontAlignY=38&desc=Projeto%20Opensource%20para%20melhorar%20o%20match%20entre%20profissionais%20Juniors%20e%20Empresas!&descAlignY=61&descAlign=52" /> +<img width="100%" src="https://capsule-render.vercel.app/api?type=waving&width=100%&color=2088f2&fontColor=ffffff&height=300§ion=header&text=Sou%20Junior&fontSize=90&animation=fadeIn&fontAlignY=38&desc=Projeto%20Opensource%20para%20melhorar%20o%20match%20entre%20profissionais%20Juniors%20e%20Empresas!&descAlignY=61&descAlign=52" alt="Banner do Projeto Sou Junior" />🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
9-9: Images should have alternate text (alt text)
null(MD045, no-alt-text)
11-11: Adicione texto alternativo (alt text) ao contador de visitantes.A imagem do contador de visitantes não tem um texto alternativo adequado.
-<p align="center">Visitantes: <img src="https://profile-counter.glitch.me/SouJunior/count.svg" /></p> +<p align="center">Visitantes: <img src="https://profile-counter.glitch.me/SouJunior/count.svg" alt="Contador de visitantes" /></p>🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
11-11: Images should have alternate text (alt text)
null(MD045, no-alt-text)
28-28: Corrija a pontuação no cabeçalho.O cabeçalho "Pré-requisitos:" termina com dois pontos, o que não segue as melhores práticas de Markdown.
-### Pré-requisitos: +### Pré-requisitos🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
28-28: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
README.md(5 hunks)src/modules/savedjobs/savedjobs.controller.ts(1 hunks)
🧰 Additional context used
🪛 ESLint
src/modules/savedjobs/savedjobs.controller.ts
[error] 1-1: Replace ·Body,·Controller,·Get,·HttpException,·HttpStatus,·Post,·Query,·UseGuards,·UsePipes,·ValidationPipe· with ⏎··Body,⏎··Controller,⏎··Get,⏎··HttpException,⏎··HttpStatus,⏎··Post,⏎··Query,⏎··UseGuards,⏎··UsePipes,⏎··ValidationPipe,⏎
(prettier/prettier)
[error] 2-2: Replace ·ApiBearerAuth,·ApiOperation,·ApiTags,·ApiResponse· with ⏎··ApiBearerAuth,⏎··ApiOperation,⏎··ApiTags,⏎··ApiResponse,⏎
(prettier/prettier)
[error] 23-23: Delete ··
(prettier/prettier)
[error] 33-33: Replace ·/*·exemplo·do·objeto·salvo·*/ with ⏎··········/*·exemplo·do·objeto·salvo·*/⏎·······
(prettier/prettier)
[error] 41-43: Replace ⏎····@Body()·createSavedJobDto:·CreateSavedJobDto,⏎·· with @Body()·createSavedJobDto:·CreateSavedJobDto
(prettier/prettier)
[error] 63-63: Replace ·summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.' with ⏎····summary:·'Obtenha·todos·os·trabalhos·salvos·com·filtros·e·paginação.',⏎·
(prettier/prettier)
[error] 69-69: Replace /*·exemplo·de·lista·de·jobs·*/ with ⏎··········/*·exemplo·de·lista·de·jobs·*/⏎········
(prettier/prettier)
[error] 85-85: Replace pageOptionsDto,·query with ⏎········pageOptionsDto,⏎········query,⏎······
(prettier/prettier)
🪛 markdownlint-cli2 (0.17.2)
README.md
9-9: Images should have alternate text (alt text)
null
(MD045, no-alt-text)
11-11: Images should have alternate text (alt text)
null
(MD045, no-alt-text)
28-28: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
🪛 LanguageTool
README.md
[uncategorized] ~139-~139: Esta locução deve ser separada por vírgulas.
Context: ...ash CREATE DATABASE "vagas-api"; ``` ✅ Agora sim, tudo certo? Siga com as migrations! `...
(VERB_COMMA_CONJUNCTION)
🔇 Additional comments (4)
README.md (4)
70-81: Excelente adição de instruções para configuração do arquivo .env!A inclusão detalhada das variáveis de ambiente necessárias e as instruções de segurança sobre não compartilhar o arquivo são muito úteis para novos desenvolvedores.
116-143: Ótima seção de solução de problemas!A adição da seção de troubleshooting é muito útil, especialmente para problemas comuns de autenticação e banco de dados que os novos desenvolvedores podem enfrentar.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~139-~139: Esta locução deve ser separada por vírgulas.
Context: ...ash CREATE DATABASE "vagas-api"; ``` ✅ Agora sim, tudo certo? Siga com as migrations! `...(VERB_COMMA_CONJUNCTION)
230-243: Excelente adição da seção "Como contribuir".A nova seção fornece instruções claras sobre como os desenvolvedores podem contribuir para o projeto, abordando desde a criação de issues até o envio de PRs.
248-348: Organização exemplar da seção de equipe.A reorganização da equipe em tabelas estruturadas por função torna o reconhecimento dos contribuidores muito mais claro e profissional.
| async getAllSavedJobs( | ||
| @Query() pageOptionsDto: PageOptionsDto, | ||
| @Query() query: GetAllSavedJobsDto, | ||
| ) { |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Defina um tipo de retorno específico para o método getAllSavedJobs.
O método getAllSavedJobs não tem um tipo de retorno explícito, o que dificulta a compreensão da API.
async getAllSavedJobs(
@Query() pageOptionsDto: PageOptionsDto,
@Query() query: GetAllSavedJobsDto,
- ) {
+ ): Promise<PageDto<SavedJobsEntity>> {Não esqueça de adicionar a importação:
import { PageDto } from 'src/shared/pagination';
import { SavedJobsEntity } from '../../database/entities/savedjobs.entity';| async saveJob( | ||
| @Body() createSavedJobDto: CreateSavedJobDto, | ||
| ): Promise<any> { |
There was a problem hiding this comment.
Falta usar o decorator @getuser() para obter o usuário autenticado.
O endpoint está protegido com AuthGuard(), mas não está utilizando as informações do usuário autenticado. Em vez de receber o userId no DTO, é mais seguro obtê-lo diretamente do token.
Aplique a seguinte alteração:
async saveJob(
@Body() createSavedJobDto: CreateSavedJobDto,
+ @GetUser() user: UsersEntity,
): Promise<any> {
try {
- const savedJob = await this.savedJobsService.saveJob(createSavedJobDto);
+ const savedJob = await this.savedJobsService.saveJob({
+ ...createSavedJobDto,
+ userId: user.id
+ });
return {
message: 'Sua vaga foi salva com sucesso!',
statusCode: HttpStatus.CREATED,
savedJob: savedJob,
};Não esqueça de adicionar as importações:
import { GetUser } from '../auth/get-user.decorator';
import { UsersEntity } from '../../database/entities/users.entity';E atualize o DTO para tornar o userId opcional:
export class CreateSavedJobDto {
- @IsUUID()
- @IsNotEmpty()
+ @IsOptional()
+ @IsUUID()
userId: string;
@IsUUID()
@IsNotEmpty()
jobId: string;
}🧰 Tools
🪛 ESLint
[error] 41-43: Replace ⏎····@Body()·createSavedJobDto:·CreateSavedJobDto,⏎·· with @Body()·createSavedJobDto:·CreateSavedJobDto
(prettier/prettier)
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/database/entities/jobs.entity.ts (1)
143-145: Correção de formatação necessária na definição do relacionamento.O relacionamento com SavedJobsEntity está corretamente implementado, mas existem problemas de formatação que devem ser corrigidos:
- Use aspas simples em vez de aspas duplas (padrão do projeto)
- Remova a linha extra após a definição de
savedJobs@OneToMany(() => SavedJobsEntity, (savedJob) => savedJob.job) - @JoinColumn({name: "jobId"}) + @JoinColumn({ name: 'jobId' }) savedJobs: SavedJobsEntity[]; -🧰 Tools
🪛 ESLint
[error] 144-144: Replace
name:·"jobId"with·name:·'jobId'·(prettier/prettier)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/database/entities/jobs.entity.ts(3 hunks)src/database/entities/savedjobs.entity.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/database/entities/savedjobs.entity.ts
🧰 Additional context used
🪛 ESLint
src/database/entities/jobs.entity.ts
[error] 144-144: Replace name:·"jobId" with ·name:·'jobId'·
(prettier/prettier)
[error] 145-146: Delete ⏎
(prettier/prettier)
🔇 Additional comments (3)
src/database/entities/jobs.entity.ts (3)
18-20: Importações adicionadas para suportar funcionalidade de vagas salvas e validação de status.As importações adicionadas estão corretas e necessárias para as novas funcionalidades. A importação do
SavedJobsEntitypermite a relação com a nova entidade, enquantoStatusEnumeIsEnumpossibilitam a validação adequada do campo de status.
120-122: Implementação adequada do enum para status com validação.Excelente implementação do campo status utilizando enum e validação com class-validator. A alteração melhora a tipagem e garante a integridade dos dados através da validação com
@IsEnum.
144-144: Verificar necessidade do @joincolumn no OneToMany.O decorador
@JoinColumngeralmente não é necessário em relacionamentos@OneToMany, pois a coluna de junção é definida no lado@ManyToOneda relação. O TypeORM automaticamente estabelece a relação inversa.Verifique na entidade
SavedJobsEntityse já existe um@ManyToOnecom@JoinColumnapontando para oJobsEntity.🧰 Tools
🪛 ESLint
[error] 144-144: Replace
name:·"jobId"with·name:·'jobId'·(prettier/prettier)
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (2)
src/app.module.ts (2)
52-52: A arquitetura do módulo parece inconsistente.O
SavedJobsEntityeJobsEntityestão sendo registrados noTypeOrmModule.forFeaturedo módulo principal, enquanto o ideal seria registrá-los apenas noSavedJobsModule. Isso está criando uma dependência desnecessária no módulo principal.Considere mover esta configuração para o módulo específico em vez de mantê-la no AppModule:
- TypeOrmModule.forFeature([UsersEntity, SavedJobsEntity, JobsEntity]), + TypeOrmModule.forFeature([UsersEntity]),
57-58: Remova o controller e o serviço do AppModule.O
SavedJobsControllere oSavedJobsServicenão deveriam estar declarados no módulo principal, pois isso viola o princípio de modularidade do NestJS. Eles deveriam estar apenas noSavedJobsModule.- controllers: [AppController, SavedJobsController], - providers: [AppService, UserRepository, SavedJobsService], + controllers: [AppController], + providers: [AppService, UserRepository],
🧹 Nitpick comments (10)
src/database/migrations/1746107506888-migrations.ts (1)
9-9: Considere usar CASCADE ou SET NULL para a foreign key.A restrição de chave estrangeira está configurada com
ON DELETE NO ACTION ON UPDATE NO ACTION, o que significa que se uma vaga for deletada, os registros de vagas salvas permanecerão no banco. Isso pode causar referências inválidas.Considere usar
ON DELETE CASCADEpara excluir automaticamente os registros de vagas salvas quando uma vaga for excluída, ouON DELETE SET NULLse quiser manter um histórico de vagas salvas.- await queryRunner.query(`ALTER TABLE "tb_saved_jobs" ADD CONSTRAINT "FK_9bbd9a1f3bb4942f0471816b111" FOREIGN KEY ("jobId") REFERENCES "tb_jobs"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "tb_saved_jobs" ADD CONSTRAINT "FK_9bbd9a1f3bb4942f0471816b111" FOREIGN KEY ("jobId") REFERENCES "tb_jobs"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);🧰 Tools
🪛 ESLint
[error] 9-9: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_9bbd9a1f3bb4942f0471816b111"·FOREIGN·KEY·("jobId")·REFERENCES·"tb_jobs"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION);with);⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_9bbd9a1f3bb4942f0471816b111"·FOREIGN·KEY·("jobId")·REFERENCES·"tb_jobs"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION,(prettier/prettier)
src/modules/savedjobs/savedjobs.module.ts (3)
3-3: Corrija o caminho de importação redundante.A importação contém múltiplos
./././.que são redundantes e podem causar confusão. Simplifique para um único./.-import { SavedJobsService } from '././././services/savedjobs.service'; +import { SavedJobsService } from './services/savedjobs.service';
19-20: Adicione vírgula após o último item do array.Falta uma vírgula após
SavedJobsCleanerServicepara manter a consistência e facilitar futuras adições ao array.SavedJobsService, FindAllSavedJobsService, SavedJobsRepository, - SavedJobsCleanerService + SavedJobsCleanerService,🧰 Tools
🪛 ESLint
[error] 19-19: Insert
,(prettier/prettier)
25-26: Adicione vírgula após o último item do array.Falta uma vírgula após
SavedJobsCleanerServicepara manter a consistência e facilitar futuras adições ao array.SavedJobsService, FindAllSavedJobsService, SavedJobsRepository, - SavedJobsCleanerService + SavedJobsCleanerService,🧰 Tools
🪛 ESLint
[error] 25-25: Insert
,(prettier/prettier)
src/modules/savedjobs/services/savedjobs-cleaner.service.ts (1)
1-5: Padronize o estilo de aspas nos imports.Os imports estão usando aspas duplas, enquanto o padrão do projeto parece ser aspas simples. Mantenha a consistência com o restante do código.
-import { Injectable, Logger } from "@nestjs/common"; -import { Cron, CronExpression } from "@nestjs/schedule"; -import { InjectRepository } from "@nestjs/typeorm"; -import { SavedJobsEntity } from "src/database/entities/savedjobs.entity"; -import { LessThan, Repository } from "typeorm"; +import { Injectable, Logger } from '@nestjs/common'; +import { Cron, CronExpression } from '@nestjs/schedule'; +import { InjectRepository } from '@nestjs/typeorm'; +import { SavedJobsEntity } from 'src/database/entities/savedjobs.entity'; +import { LessThan, Repository } from 'typeorm';🧰 Tools
🪛 ESLint
[error] 1-1: Replace
"@nestjs/common";␍with'@nestjs/common';(prettier/prettier)
[error] 2-2: Replace
"@nestjs/schedule";␍with'@nestjs/schedule';(prettier/prettier)
[error] 3-3: Replace
"@nestjs/typeorm";␍with'@nestjs/typeorm';(prettier/prettier)
[error] 4-4: Replace
"src/database/entities/savedjobs.entity";␍with'src/database/entities/savedjobs.entity';(prettier/prettier)
[error] 5-5: Replace
"typeorm";␍with'typeorm';(prettier/prettier)
src/app.module.ts (1)
21-27: Adicione ponto e vírgula nos imports.Adicione ponto e vírgula após as declarações de importação para manter a consistência.
-import { SavedJobsEntity } from './database/entities/savedjobs.entity' +import { SavedJobsEntity } from './database/entities/savedjobs.entity'; -import { SavedJobsController } from './modules/savedjobs/savedjobs.controller' +import { SavedJobsController } from './modules/savedjobs/savedjobs.controller'; -import { JobsEntity } from './database/entities/jobs.entity'; +import { JobsEntity } from './database/entities/jobs.entity'; -import { SavedJobsModule } from './modules/savedjobs/savedjobs.module' +import { SavedJobsModule } from './modules/savedjobs/savedjobs.module';🧰 Tools
🪛 ESLint
[error] 21-21: Insert
;(prettier/prettier)
[error] 25-25: Insert
;(prettier/prettier)
[error] 26-26: Delete
·(prettier/prettier)
[error] 27-27: Insert
;(prettier/prettier)
src/modules/savedjobs/services/savedjobs.service.ts (2)
53-56: Timezone consistente para datas
new Date()depende do timezone da instância de aplicação. Para manter coerência com o campo default do banco (que usanow()em UTC), prefira:const savedAt = new Date(Date.now());ou gerenciar via banco (
DEFAULT now()) e não atribuir manualmente.
Isso evita divergências de horário em servidores com timezones distintos.
66-69: Consulta extra apóssavepode ser evitada
save()já devolve a entidade recém-persistida. Se você precisa apenas das relações, utilizethis.savedJobsRepository.save(newSavedJob, { reload: true })ouawait this.savedJobsRepository.findOne({ ... })comloadRelationIdsem vez de executar outra busca completa.Menos uma query melhora o throughput do endpoint.
src/database/migrations/1745261990419-migrations.ts (1)
8-8: Índice para expiração melhora o cleaner jobComo estamos utilizando
expiresAtpara remoção periódica, adicionar um índice (CREATE INDEX idx_saved_jobs_expires ON tb_saved_jobs (expiresAt);) tornará a deleção/consulta muito mais eficiente em grandes volumes.🧰 Tools
🪛 ESLint
[error] 8-8: Replace
····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now()·+·interval·'7·days');with);⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now()·+·interval·'7·days',(prettier/prettier)
src/modules/savedjobs/repository/savedjobs.repository.ts (1)
40-44: Evite vazar mensagens internas no erro 500
InternalServerErrorExceptionrecebeerror.message, que muitas vezes contém detalhes do banco. Sugestão:-throw new InternalServerErrorException(`Erro ao buscar vagas salvas: ${error.message}`); +throw new InternalServerErrorException('Erro interno ao buscar vagas salvas');Logue a exception completa em nível de infraestrutura (Interceptor ou Logger), mas devolva mensagem genérica para o cliente.
🧰 Tools
🪛 ESLint
[error] 40-40: Delete
␍(prettier/prettier)
[error] 41-41: Delete
␍(prettier/prettier)
[error] 42-42: Delete
␍(prettier/prettier)
[error] 43-43: Delete
␍(prettier/prettier)
[error] 44-44: Delete
␍(prettier/prettier)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (9)
package.json(1 hunks)src/app.module.ts(2 hunks)src/database/migrations/1745261990419-migrations.ts(1 hunks)src/database/migrations/1746107506888-migrations.ts(1 hunks)src/main.ts(1 hunks)src/modules/savedjobs/repository/savedjobs.repository.ts(1 hunks)src/modules/savedjobs/savedjobs.module.ts(1 hunks)src/modules/savedjobs/services/savedjobs-cleaner.service.ts(1 hunks)src/modules/savedjobs/services/savedjobs.service.ts(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- package.json
- src/main.ts
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/modules/savedjobs/services/savedjobs-cleaner.service.ts (2)
src/modules/savedjobs/repository/savedjobs.repository.ts (1)
Injectable(8-46)src/modules/savedjobs/services/savedjobs.service.ts (1)
Injectable(12-71)
src/app.module.ts (1)
src/modules/savedjobs/savedjobs.module.ts (1)
Module(11-28)
src/modules/savedjobs/savedjobs.module.ts (1)
src/app.module.ts (1)
Module(30-60)
🪛 ESLint
src/database/migrations/1746107506888-migrations.ts
[error] 1-1: Replace "typeorm" with 'typeorm'
(prettier/prettier)
[error] 4-4: Replace ··name·=·'Migrations1746107506888' with name·=·'Migrations1746107506888';
(prettier/prettier)
[error] 6-6: Delete ··
(prettier/prettier)
[error] 7-7: Replace ········await·queryRunner.query(CREATE·INDEX·"IDX_3845b06bfda63ccc1da359b378"·ON·"tb_saved_jobs"·("userId")·); with ····await·queryRunner.query(⏎······CREATE·INDEX·"IDX_3845b06bfda63ccc1da359b378"·ON·"tb_saved_jobs"·("userId")·,
(prettier/prettier)
[error] 8-8: Replace ····await·queryRunner.query(CREATE·INDEX·"IDX_9bbd9a1f3bb4942f0471816b11"·ON·"tb_saved_jobs"·("jobId")·); with );⏎····await·queryRunner.query(⏎······CREATE·INDEX·"IDX_9bbd9a1f3bb4942f0471816b11"·ON·"tb_saved_jobs"·("jobId")·,
(prettier/prettier)
[error] 9-9: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_9bbd9a1f3bb4942f0471816b111"·FOREIGN·KEY·("jobId")·REFERENCES·"tb_jobs"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_9bbd9a1f3bb4942f0471816b111"·FOREIGN·KEY·("jobId")·REFERENCES·"tb_jobs"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION,
(prettier/prettier)
[error] 10-11: Replace }⏎ with );
(prettier/prettier)
[error] 12-12: Insert }⏎⏎
(prettier/prettier)
[error] 13-13: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_9bbd9a1f3bb4942f0471816b111"); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_9bbd9a1f3bb4942f0471816b111",
(prettier/prettier)
[error] 14-14: Replace ····await·queryRunner.query(DROP·INDEX·"public"."IDX_9bbd9a1f3bb4942f0471816b11"); with );⏎····await·queryRunner.query(⏎······DROP·INDEX·"public"."IDX_9bbd9a1f3bb4942f0471816b11",
(prettier/prettier)
[error] 15-15: Replace ····await·queryRunner.query(DROP·INDEX·"public"."IDX_3845b06bfda63ccc1da359b378"`` with );⏎····await·queryRunner.query(⏎······DROP·INDEX·"public"."IDX_3845b06bfda63ccc1da359b378"`,⏎····`
(prettier/prettier)
[error] 16-17: Replace ··}⏎ with }
(prettier/prettier)
src/modules/savedjobs/services/savedjobs-cleaner.service.ts
[error] 1-1: Replace "@nestjs/common";␍ with '@nestjs/common';
(prettier/prettier)
[error] 2-2: Replace "@nestjs/schedule";␍ with '@nestjs/schedule';
(prettier/prettier)
[error] 3-3: Replace "@nestjs/typeorm";␍ with '@nestjs/typeorm';
(prettier/prettier)
[error] 4-4: Replace "src/database/entities/savedjobs.entity";␍ with 'src/database/entities/savedjobs.entity';
(prettier/prettier)
[error] 5-5: Replace "typeorm";␍ with 'typeorm';
(prettier/prettier)
[error] 6-6: Delete ␍
(prettier/prettier)
[error] 7-7: Delete ␍
(prettier/prettier)
[error] 8-8: Delete ␍
(prettier/prettier)
[error] 9-9: Delete ␍
(prettier/prettier)
[error] 10-10: Delete ␍
(prettier/prettier)
[error] 11-11: Delete ␍
(prettier/prettier)
[error] 12-12: Delete ␍
(prettier/prettier)
[error] 13-13: Delete ␍
(prettier/prettier)
[error] 14-14: Delete ␍
(prettier/prettier)
[error] 15-15: Delete ␍
(prettier/prettier)
[error] 16-16: Delete ␍
(prettier/prettier)
[error] 17-17: Delete ␍
(prettier/prettier)
[error] 18-18: Delete ␍
(prettier/prettier)
[error] 19-19: Delete ␍
(prettier/prettier)
[error] 20-20: Delete ␍
(prettier/prettier)
[error] 21-21: Delete ␍
(prettier/prettier)
[error] 22-22: Delete ␍
(prettier/prettier)
[error] 23-23: Delete ␍
(prettier/prettier)
[error] 24-24: Delete ␍
(prettier/prettier)
[error] 25-25: Delete ␍
(prettier/prettier)
src/app.module.ts
[error] 21-21: Insert ;
(prettier/prettier)
[error] 25-25: Insert ;
(prettier/prettier)
[error] 26-26: Delete ·
(prettier/prettier)
[error] 27-27: Insert ;
(prettier/prettier)
src/database/migrations/1745261990419-migrations.ts
[error] 1-1: Replace "typeorm" with 'typeorm'
(prettier/prettier)
[error] 4-4: Replace ····name·=·'Migrations1745261990419' with ··name·=·'Migrations1745261990419';
(prettier/prettier)
[error] 6-6: Delete ··
(prettier/prettier)
[error] 7-7: Replace ········await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"); with ····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a",
(prettier/prettier)
[error] 8-8: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now()·+·interval·'7·days'); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now()·+·interval·'7·days',
(prettier/prettier)
[error] 9-9: Insert );⏎
(prettier/prettier)
[error] 10-10: Delete ····
(prettier/prettier)
[error] 11-11: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL,
(prettier/prettier)
[error] 12-12: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION,
(prettier/prettier)
[error] 13-14: Replace }⏎ with );
(prettier/prettier)
[error] 15-15: Insert }⏎⏎
(prettier/prettier)
[error] 16-16: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a",
(prettier/prettier)
[error] 17-17: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·DROP·NOT·NULL); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·DROP·NOT·NULL,
(prettier/prettier)
[error] 18-18: Insert );⏎
(prettier/prettier)
[error] 19-19: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"jobId"·character·varying·NOT·NULL); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·"jobId"·character·varying·NOT·NULL,
(prettier/prettier)
[error] 20-20: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·DROP·COLUMN·"expiresAt"); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·DROP·COLUMN·"expiresAt",
(prettier/prettier)
[error] 21-21: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION`` with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·NO·ACTION·ON·UPDATE·NO·ACTION`,⏎····`
(prettier/prettier)
[error] 22-23: Replace ··}⏎ with }
(prettier/prettier)
src/modules/savedjobs/repository/savedjobs.repository.ts
[error] 1-1: Replace "@nestjs/common";␍ with '@nestjs/common';
(prettier/prettier)
[error] 2-2: Replace "@nestjs/typeorm";␍ with '@nestjs/typeorm';
(prettier/prettier)
[error] 3-3: Replace "src/database/entities/savedjobs.entity";␍ with 'src/database/entities/savedjobs.entity';
(prettier/prettier)
[error] 4-4: Replace "src/shared/pagination";␍ with 'src/shared/pagination';
(prettier/prettier)
[error] 5-5: Replace "typeorm";␍ with 'typeorm';
(prettier/prettier)
[error] 6-6: Replace "../dtos/get-all-savedjobs.dto";␍ with '../dtos/get-all-savedjobs.dto';
(prettier/prettier)
[error] 7-7: Delete ␍
(prettier/prettier)
[error] 8-8: Delete ␍
(prettier/prettier)
[error] 9-9: Delete ␍
(prettier/prettier)
[error] 10-10: Delete ␍
(prettier/prettier)
[error] 11-11: Delete ␍
(prettier/prettier)
[error] 12-12: Delete ␍
(prettier/prettier)
[error] 13-13: Delete ␍
(prettier/prettier)
[error] 14-14: Delete ␍
(prettier/prettier)
[error] 15-15: Delete ␍
(prettier/prettier)
[error] 16-16: Delete ␍
(prettier/prettier)
[error] 17-17: Delete ␍
(prettier/prettier)
[error] 18-18: Delete ␍
(prettier/prettier)
[error] 19-19: Delete ␍
(prettier/prettier)
[error] 20-20: Delete ␍
(prettier/prettier)
[error] 21-21: Delete ␍
(prettier/prettier)
[error] 22-22: Delete ␍
(prettier/prettier)
[error] 23-23: Delete ␍
(prettier/prettier)
[error] 24-24: Delete ␍
(prettier/prettier)
[error] 25-25: Delete ␍
(prettier/prettier)
[error] 26-26: Delete ␍
(prettier/prettier)
[error] 27-27: Delete ␍
(prettier/prettier)
[error] 28-28: Delete ␍
(prettier/prettier)
[error] 29-29: Delete ␍
(prettier/prettier)
[error] 30-30: Delete ␍
(prettier/prettier)
[error] 31-31: Delete ␍
(prettier/prettier)
[error] 32-32: Delete ␍
(prettier/prettier)
[error] 33-33: Delete ␍
(prettier/prettier)
[error] 34-34: Delete ␍
(prettier/prettier)
[error] 35-35: Delete ␍
(prettier/prettier)
[error] 36-36: Delete ␍
(prettier/prettier)
[error] 37-37: Delete ␍
(prettier/prettier)
[error] 38-38: Delete ␍
(prettier/prettier)
[error] 39-39: Delete ␍
(prettier/prettier)
[error] 40-40: Delete ␍
(prettier/prettier)
[error] 41-41: Delete ␍
(prettier/prettier)
[error] 42-42: Delete ␍
(prettier/prettier)
[error] 43-43: Delete ␍
(prettier/prettier)
[error] 44-44: Delete ␍
(prettier/prettier)
[error] 45-45: Delete ␍
(prettier/prettier)
[error] 46-46: Insert ⏎
(prettier/prettier)
src/modules/savedjobs/savedjobs.module.ts
[error] 13-13: Delete ·
(prettier/prettier)
[error] 18-18: Delete ·
(prettier/prettier)
[error] 19-19: Insert ,
(prettier/prettier)
[error] 24-24: Delete ·
(prettier/prettier)
[error] 25-25: Insert ,
(prettier/prettier)
[error] 28-28: Insert ⏎
(prettier/prettier)
src/modules/savedjobs/services/savedjobs.service.ts
[error] 1-4: Replace ⏎··BadRequestException,⏎··Injectable,⏎ with ·BadRequestException,·Injectable·
(prettier/prettier)
[error] 23-24: Delete ⏎
(prettier/prettier)
[error] 26-26: Replace createSavedJobDto:·CreateSavedJobDto with ⏎····createSavedJobDto:·CreateSavedJobDto,⏎··
(prettier/prettier)
[error] 29-29: Replace ·where:·{·id:·userId·} with ⏎······where:·{·id:·userId·},⏎···
(prettier/prettier)
🔇 Additional comments (1)
src/modules/savedjobs/repository/savedjobs.repository.ts (1)
24-26: Ordenação default pode gerar SQL inválido
pageOptionsDto.orderdeve limitar-se a'ASC' | 'DESC'. Garanta no DTO/enum essa restrição para evitar SQL injection indireto e erros de sintaxe ao chegar valores arbitrários.🧰 Tools
🪛 ESLint
[error] 24-24: Delete
␍(prettier/prettier)
[error] 25-25: Delete
␍(prettier/prettier)
[error] 26-26: Delete
␍(prettier/prettier)
| @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) | ||
| async cleanExpiredJobs() { | ||
| const now = new Date(); | ||
| const result = await this.savedJobsRepository.delete({ | ||
| expiresAt: LessThan(now), | ||
| }); | ||
|
|
||
| this.logger.log(`🧹 Removidos ${result.affected} registros expirados.`); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Adicione tratamento de erros ao método cleanExpiredJobs.
O método atual não possui tratamento de erros, o que pode causar falhas silenciosas se a operação de exclusão falhar.
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
async cleanExpiredJobs() {
- const now = new Date();
- const result = await this.savedJobsRepository.delete({
- expiresAt: LessThan(now),
- });
-
- this.logger.log(`🧹 Removidos ${result.affected} registros expirados.`);
+ try {
+ const now = new Date();
+ const result = await this.savedJobsRepository.delete({
+ expiresAt: LessThan(now),
+ });
+
+ this.logger.log(`🧹 Removidos ${result.affected} registros expirados.`);
+ } catch (error) {
+ this.logger.error(`Erro ao limpar registros expirados: ${error.message}`, error.stack);
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) | |
| async cleanExpiredJobs() { | |
| const now = new Date(); | |
| const result = await this.savedJobsRepository.delete({ | |
| expiresAt: LessThan(now), | |
| }); | |
| this.logger.log(`🧹 Removidos ${result.affected} registros expirados.`); | |
| } | |
| @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) | |
| async cleanExpiredJobs() { | |
| try { | |
| const now = new Date(); | |
| const result = await this.savedJobsRepository.delete({ | |
| expiresAt: LessThan(now), | |
| }); | |
| this.logger.log(`🧹 Removidos ${result.affected} registros expirados.`); | |
| } catch (error) { | |
| this.logger.error( | |
| `Erro ao limpar registros expirados: ${error.message}`, | |
| error.stack, | |
| ); | |
| } | |
| } |
🧰 Tools
🪛 ESLint
[error] 16-16: Delete ␍
(prettier/prettier)
[error] 17-17: Delete ␍
(prettier/prettier)
[error] 18-18: Delete ␍
(prettier/prettier)
[error] 19-19: Delete ␍
(prettier/prettier)
[error] 20-20: Delete ␍
(prettier/prettier)
[error] 21-21: Delete ␍
(prettier/prettier)
[error] 22-22: Delete ␍
(prettier/prettier)
[error] 23-23: Delete ␍
(prettier/prettier)
[error] 24-24: Delete ␍
(prettier/prettier)
| const alreadySaved = await this.savedJobsRepository.exist({ | ||
| where: { | ||
| user: { id: userId }, | ||
| job: { id: jobId }, | ||
| }, | ||
| }); | ||
|
|
||
| if (alreadySaved) { | ||
| throw new BadRequestException('Esta vaga já foi salva por este usuário.'); | ||
| } |
There was a problem hiding this comment.
Concorrência ainda pode gerar duplicidade de registros
Mesmo com a verificação alreadySaved, duas requisições simultâneas podem passar nessa checagem antes que uma delas persista o registro. Recomenda-se:
- Criar uma constraint
UNIQUE (userId, jobId)na tabelatb_saved_jobs(atualmente não há). - Envolver o bloco de persistência em transação e capturar a exceção de violação de unicidade, retornando 409/BadRequest apropriado.
Sem isso, continuamos sujeitos a registros duplicados.
| const userExists = await this.usersRepository.exist({ where: { id: userId } }); | ||
| if (!userExists) { | ||
| throw new BadRequestException('Usuário não encontrado.'); | ||
| } | ||
|
|
||
| const jobExists = await this.jobsRepository.exist({ where: { id: jobId } }); | ||
| if (!jobExists) { | ||
| throw new BadRequestException('Vaga não encontrada.'); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Evite consultas redundantes e reduza round-trips ao banco
Os métodos exist() checam a presença de usuário e vaga, mas logo em seguida (linhas 50-52) são feitos dois findOneBy para buscar as mesmas entidades. Podemos economizar duas viagens ao banco buscando o usuário e a vaga diretamente e, a partir delas, validar a existência:
-const userExists = await this.usersRepository.exist({ where: { id: userId } });
-if (!userExists) {
- throw new BadRequestException('Usuário não encontrado.');
-}
-const jobExists = await this.jobsRepository.exist({ where: { id: jobId } });
-if (!jobExists) {
- throw new BadRequestException('Vaga não encontrada.');
-}
-
-const user = await this.usersRepository.findOneBy({ id: userId });
-const job = await this.jobsRepository.findOneBy({ id: jobId });
+const user = await this.usersRepository.findOne({ where: { id: userId } });
+if (!user) {
+ throw new BadRequestException('Usuário não encontrado.');
+}
+const job = await this.jobsRepository.findOne({ where: { id: jobId } });
+if (!job) {
+ throw new BadRequestException('Vaga não encontrada.');
+}Além do ganho de performance, diminuímos a chance de resultados inconsistentes em cenários de replicação.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const userExists = await this.usersRepository.exist({ where: { id: userId } }); | |
| if (!userExists) { | |
| throw new BadRequestException('Usuário não encontrado.'); | |
| } | |
| const jobExists = await this.jobsRepository.exist({ where: { id: jobId } }); | |
| if (!jobExists) { | |
| throw new BadRequestException('Vaga não encontrada.'); | |
| } | |
| // Replace redundant exist() + findOneBy() calls with a single findOne() per entity | |
| const user = await this.usersRepository.findOne({ where: { id: userId } }); | |
| if (!user) { | |
| throw new BadRequestException('Usuário não encontrado.'); | |
| } | |
| const job = await this.jobsRepository.findOne({ where: { id: jobId } }); | |
| if (!job) { | |
| throw new BadRequestException('Vaga não encontrada.'); | |
| } |
🧰 Tools
🪛 ESLint
[error] 29-29: Replace ·where:·{·id:·userId·} with ⏎······where:·{·id:·userId·},⏎···
(prettier/prettier)
| await queryRunner.query(`ALTER TABLE "tb_saved_jobs" ADD "expiresAt" TIMESTAMP NOT NULL DEFAULT now() + interval '7 days'`); | ||
| await queryRunner.query(`ALTER TABLE "tb_saved_jobs" DROP COLUMN "jobId"`); | ||
| await queryRunner.query(`ALTER TABLE "tb_saved_jobs" ADD "jobId" uuid`); | ||
| await queryRunner.query(`ALTER TABLE "tb_saved_jobs" ALTER COLUMN "userId" SET NOT NULL`); | ||
| await queryRunner.query(`ALTER TABLE "tb_saved_jobs" ADD CONSTRAINT "FK_3845b06bfda63ccc1da359b378a" FOREIGN KEY ("userId") REFERENCES "tb_users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); |
There was a problem hiding this comment.
jobId foi recriado sem NOT NULL nem chave estrangeira
Ao dropar e adicionar novamente a coluna:
ALTER TABLE "tb_saved_jobs" ADD "jobId" uuid- A coluna agora aceita
NULL, mas o domínio da aplicação exige presença dojob. - Não há
FOREIGN KEYparatb_jobs(id), dificultando integridade referencial e exclusão em cascata.
Sugestão de correção:
ALTER TABLE "tb_saved_jobs"
ADD "jobId" uuid NOT NULL,
ADD CONSTRAINT "FK_saved_jobs_job"
FOREIGN KEY ("jobId") REFERENCES "tb_jobs"("id")
ON DELETE CASCADE;Sem esses ajustes, poderemos ter registros órfãos ou falha de insert em runtime.
🧰 Tools
🪛 ESLint
[error] 8-8: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now()·+·interval·'7·days'); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·"expiresAt"·TIMESTAMP·NOT·NULL·DEFAULT·now()·+·interval·'7·days',
(prettier/prettier)
[error] 9-9: Insert );⏎
(prettier/prettier)
[error] 10-10: Delete ····
(prettier/prettier)
[error] 11-11: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL); with await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ALTER·COLUMN·"userId"·SET·NOT·NULL,
(prettier/prettier)
[error] 12-12: Replace ····await·queryRunner.query(ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION); with );⏎····await·queryRunner.query(⏎······ALTER·TABLE·"tb_saved_jobs"·ADD·CONSTRAINT·"FK_3845b06bfda63ccc1da359b378a"·FOREIGN·KEY·("userId")·REFERENCES·"tb_users"("id")·ON·DELETE·CASCADE·ON·UPDATE·NO·ACTION,
(prettier/prettier)
| if (filters.userId) { | ||
| queryBuilder.andWhere('user.id = :userId', { userId: filters.userId }); | ||
| } | ||
|
|
||
| if (filters.jobId) { | ||
| queryBuilder.andWhere('job.id = :jobId', { jobId: filters.jobId }); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Valide UUIDs recebidos nos filtros
Os campos filters.userId e filters.jobId são interpolados direto na query. Ainda que usemos parâmetros, valores inválidos (ex.: strings aleatórias) resultarão em erro 500 do banco. Inclua validação antecipada (class-validator IsUUID) no DTO ou trate aqui lançando BadRequestException.
🧰 Tools
🪛 ESLint
[error] 28-28: Delete ␍
(prettier/prettier)
[error] 29-29: Delete ␍
(prettier/prettier)
[error] 30-30: Delete ␍
(prettier/prettier)
[error] 31-31: Delete ␍
(prettier/prettier)
[error] 32-32: Delete ␍
(prettier/prettier)
[error] 33-33: Delete ␍
(prettier/prettier)
[error] 34-34: Delete ␍
(prettier/prettier)
MikaelMelo1
left a comment
There was a problem hiding this comment.
Tudo certo viu, só uma observação no tratamento de erro no savedjobs-cleaner.service mas não solicitei
|
There was a problem hiding this comment.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (64)
src/app.controller.ts(1 hunks)src/app.module.ts(2 hunks)src/app.service.ts(1 hunks)src/database/data-source.ts(1 hunks)src/database/entities/applications.entity.ts(1 hunks)src/database/entities/comments.entity.ts(1 hunks)src/database/entities/curriculum.entity.ts(1 hunks)src/database/entities/jobs.entity.ts(3 hunks)src/database/entities/users.entity.ts(3 hunks)src/database/migrations/1709068822100-migrations.ts(1 hunks)src/database/migrations/1742707816058-migrations.ts(1 hunks)src/database/migrations/1745261990419-migrations.ts(1 hunks)src/database/migrations/1745262789787-migrations.ts(1 hunks)src/database/migrations/1745267234389-migrations.ts(1 hunks)src/database/migrations/1746107506888-migrations.ts(1 hunks)src/main.ts(2 hunks)src/modules/applications/applications.module.ts(1 hunks)src/modules/applications/interfaces/interfaces.ts(1 hunks)src/modules/applications/repository/applications.repository.ts(1 hunks)src/modules/candidacy/service/candidacy.service.spec.ts(1 hunks)src/modules/comment/comment.controller.ts(1 hunks)src/modules/comment/comment.module.ts(1 hunks)src/modules/comment/interfaces/interfaces.ts(1 hunks)src/modules/comment/repository/comment.repository.ts(1 hunks)src/modules/company/company.controller.ts(2 hunks)src/modules/company/company.module.ts(1 hunks)src/modules/company/interfaces/interfaces.ts(1 hunks)src/modules/company/repository/company-repository.ts(6 hunks)src/modules/company/services/create-company.service.ts(1 hunks)src/modules/company/services/update-password-by-email.service.ts(1 hunks)src/modules/curriculum/curriculum.controller.ts(1 hunks)src/modules/curriculum/curriculum.module.ts(1 hunks)src/modules/curriculum/curriculum.service.ts(1 hunks)src/modules/curriculum/interfaces/interfaces.ts(1 hunks)src/modules/curriculum/repository/curriculum-repository.ts(1 hunks)src/modules/jobs/interfaces/interfaces.ts(1 hunks)src/modules/jobs/jobs.controller.ts(2 hunks)src/modules/jobs/jobs.module.ts(1 hunks)src/modules/jobs/repository/job.repository.ts(3 hunks)src/modules/jobs/services/delete-job.service.ts(1 hunks)src/modules/jobs/services/get-all-jobs-from-logged-company.service.ts(1 hunks)src/modules/mails/mail.module.ts(1 hunks)src/modules/reports/reports.module.ts(1 hunks)src/modules/reports/repository/reports.repository.ts(1 hunks)src/modules/savedjobs/dtos/create-savedJob-dto.ts(1 hunks)src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts(1 hunks)src/modules/savedjobs/repository/savedjobs.repository.ts(1 hunks)src/modules/savedjobs/savedjobs.controller.ts(1 hunks)src/modules/savedjobs/savedjobs.module.ts(1 hunks)src/modules/savedjobs/services/find-all-savedjobs.service.ts(1 hunks)src/modules/savedjobs/services/savedjobs-cleaner.service.ts(1 hunks)src/modules/savedjobs/services/savedjobs.service.ts(1 hunks)src/modules/user/dtos/create-user.dto.ts(1 hunks)src/modules/user/interfaces/interfaces.ts(1 hunks)src/modules/user/repository/user.repository.ts(4 hunks)src/modules/user/services/create-user.service.ts(1 hunks)src/modules/user/services/delete-user.service.ts(1 hunks)src/modules/user/user.controller.ts(2 hunks)src/modules/user/user.module.ts(1 hunks)src/shared/Swagger/conflict.swagger.ts(1 hunks)src/shared/Swagger/decorators/app/classes/health-check-response.swagger.ts(1 hunks)src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts(1 hunks)src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts(1 hunks)src/shared/interfaces/interfaces.ts(1 hunks)
✅ Files skipped from review due to trivial changes (43)
- src/database/data-source.ts
- src/modules/applications/repository/applications.repository.ts
- src/modules/company/services/create-company.service.ts
- src/modules/jobs/jobs.module.ts
- src/modules/company/company.module.ts
- src/modules/user/user.module.ts
- src/modules/company/services/update-password-by-email.service.ts
- src/modules/curriculum/curriculum.controller.ts
- src/modules/applications/applications.module.ts
- src/database/entities/comments.entity.ts
- src/modules/candidacy/service/candidacy.service.spec.ts
- src/modules/user/services/create-user.service.ts
- src/database/entities/applications.entity.ts
- src/modules/comment/comment.controller.ts
- src/app.controller.ts
- src/modules/curriculum/curriculum.service.ts
- src/modules/curriculum/curriculum.module.ts
- src/shared/Swagger/decorators/app/classes/health-check-response.swagger.ts
- src/modules/comment/interfaces/interfaces.ts
- src/modules/curriculum/repository/curriculum-repository.ts
- src/shared/interfaces/interfaces.ts
- src/database/entities/curriculum.entity.ts
- src/modules/user/user.controller.ts
- src/modules/reports/reports.module.ts
- src/modules/user/dtos/create-user.dto.ts
- src/modules/user/services/delete-user.service.ts
- src/modules/curriculum/interfaces/interfaces.ts
- src/modules/comment/comment.module.ts
- src/modules/company/company.controller.ts
- src/modules/reports/repository/reports.repository.ts
- src/shared/Swagger/conflict.swagger.ts
- src/modules/comment/repository/comment.repository.ts
- src/modules/applications/interfaces/interfaces.ts
- src/modules/jobs/interfaces/interfaces.ts
- src/modules/jobs/jobs.controller.ts
- src/modules/jobs/services/delete-job.service.ts
- src/database/migrations/1709068822100-migrations.ts
- src/app.service.ts
- src/modules/jobs/services/get-all-jobs-from-logged-company.service.ts
- src/modules/jobs/repository/job.repository.ts
- src/modules/company/interfaces/interfaces.ts
- src/modules/user/interfaces/interfaces.ts
- src/modules/savedjobs/services/savedjobs-cleaner.service.ts
🚧 Files skipped from review as they are similar to previous changes (19)
- src/modules/mails/mail.module.ts
- src/shared/Swagger/decorators/savedjobs/view-savedjobs.swagger.decorator.ts
- src/main.ts
- src/modules/savedjobs/dtos/create-savedJob-dto.ts
- src/database/migrations/1746107506888-migrations.ts
- src/database/migrations/1745267234389-migrations.ts
- src/database/entities/users.entity.ts
- src/modules/savedjobs/savedjobs.module.ts
- src/modules/savedjobs/dtos/get-all-savedjobs.dto.ts
- src/app.module.ts
- src/shared/Swagger/decorators/savedjobs/create-savedjobs.swagger.decorator.ts
- src/database/entities/jobs.entity.ts
- src/modules/savedjobs/services/savedjobs.service.ts
- src/database/migrations/1745262789787-migrations.ts
- src/database/migrations/1742707816058-migrations.ts
- src/database/migrations/1745261990419-migrations.ts
- src/modules/savedjobs/repository/savedjobs.repository.ts
- src/modules/savedjobs/services/find-all-savedjobs.service.ts
- src/modules/savedjobs/savedjobs.controller.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/modules/company/repository/company-repository.ts (2)
src/shared/utils/handle-error.util.ts (1)
handleError(3-14)src/modules/company/dtos/update-my-password.dto.ts (1)
UpdateMyPasswordDto(5-39)
src/modules/user/repository/user.repository.ts (1)
src/shared/utils/handle-error.util.ts (1)
handleError(3-14)
🪛 Biome (1.9.4)
src/modules/company/repository/company-repository.ts
[error] 154-154: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
src/modules/user/repository/user.repository.ts
[error] 132-132: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
🔇 Additional comments (5)
src/modules/company/repository/company-repository.ts (3)
12-15: Melhoria na formatação do construtor aprovada.A formatação expandida do construtor melhora significativamente a legibilidade do código.
47-56: Formatação aprimorada dos métodos encadeados.A expansão das chamadas de métodos encadeados em múltiplas linhas melhora a legibilidade e facilita a manutenção do código.
59-70: Aprovação das melhorias de formatação.As reformatações dos métodos encadeados em múltiplas linhas melhoram consistentemente a legibilidade do código em todo o arquivo.
Also applies to: 82-88, 92-92, 96-107, 116-118, 134-136, 146-163
src/modules/user/repository/user.repository.ts (2)
17-20: Melhoria na formatação do construtor aprovada.A formatação expandida do construtor melhora a legibilidade do código de forma consistente com outros repositórios.
46-48: Aprovação das melhorias de formatação.As reformatações dos métodos encadeados em múltiplas linhas melhoram consistentemente a legibilidade e manutenibilidade do código em todo o repositório.
Also applies to: 52-52, 56-56, 76-78, 84-87, 93-95, 104-106, 110-114, 118-120, 124-126, 134-141
| return company; | ||
| } | ||
|
|
||
| async UpdateCompanyById(id: string, data: any) { |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Corrigir convenção de nomenclatura do método.
O nome do método UpdateCompanyById não segue a convenção camelCase do JavaScript/TypeScript.
- async UpdateCompanyById(id: string, data: any) {
+ async updateCompanyById(id: string, data: any) {🤖 Prompt for AI Agents
In src/modules/company/repository/company-repository.ts at line 58, the method
name UpdateCompanyById does not follow JavaScript/TypeScript camelCase naming
conventions. Rename the method to updateCompanyById to comply with standard
naming practices.
| password, | ||
| }; | ||
|
|
||
| delete company.password; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Otimizar performance removendo o operador delete.
O operador delete pode impactar a performance. Recomenda-se usar atribuição undefined conforme sugerido pela análise estática.
- delete company.password;
+ company.password = undefined;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| delete company.password; | |
| company.password = undefined; |
🧰 Tools
🪛 Biome (1.9.4)
[error] 154-154: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
🤖 Prompt for AI Agents
In src/modules/company/repository/company-repository.ts at line 154, replace the
use of the delete operator on company.password with an assignment of undefined
to company.password to optimize performance and comply with static analysis
recommendations.
| password, | ||
| }; | ||
|
|
||
| delete user.password; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Otimizar performance removendo o operador delete.
O operador delete pode impactar a performance. Recomenda-se usar atribuição undefined conforme sugerido pela análise estática.
- delete user.password;
+ user.password = undefined;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| delete user.password; | |
| user.password = undefined; |
🧰 Tools
🪛 Biome (1.9.4)
[error] 132-132: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
🤖 Prompt for AI Agents
In src/modules/user/repository/user.repository.ts at line 132, replace the use
of the delete operator on user.password with an assignment of undefined to
user.password. This change avoids the performance cost associated with delete
while effectively removing the password property value.


Descrição
Atualização do README.md com informações relevantes para novos integrantes do projeto, incluindo instruções detalhadas para configuração do ambiente local e resolução de possíveis erros de integração.
Implementado o endpoint GET para listar todas as vagas salvas.
Ajuste na autenticação da criação de vagas salvas, que acabou não sendo tratado na tarefa anterior realizada pelo colega José. Como forma de otimizar a entrega, o ajuste foi realizado diretamente nesta branch.
Summary by CodeRabbit
Novos Recursos
Documentação
Ajustes de Estilo e Refatoração
Testes
Chores