finalizando implementacao e testes da criacao de rascunhos em vaga#203
finalizando implementacao e testes da criacao de rascunhos em vaga#203lucaserib wants to merge 1 commit intoSouJunior:mainfrom
Conversation
WalkthroughEste pull request implementa um sistema de rascunho e publicação de vagas com workflow de estados. Adiciona novos campos à entidade de vagas, cria uma tabela para vagas salvas, implementa um guard de autorização para proprietários de vagas, adiciona novos DTOs e serviços para gerenciar o ciclo de vida das vagas, e atualiza a configuração Docker com o serviço Mailhog e ajustes de nomenclatura. Changes
Sequence Diagram(s)sequenceDiagram
actor User as Usuário (Empresa)
participant Ctrl as Controller
participant Guard as JobOwnerGuard
participant Service as Job Service
participant Repo as Repository
participant DB as Database
Note over User,DB: Workflow de Vaga: Criar → Publicar → Cancelar
User->>Ctrl: POST /job/draft (CreateJobDraftDto)
activate Ctrl
Ctrl->>Service: CreateJobDraftService.execute()
activate Service
Service->>Repo: createJobDraft(data, company_id)
activate Repo
Repo->>DB: INSERT vaga (jobStatus=DRAFT)
DB-->>Repo: JobsEntity {id, status=DRAFT}
Repo-->>Service: JobsEntity
deactivate Repo
Service-->>Ctrl: {id, status, message}
deactivate Service
Ctrl-->>User: 201 Created
deactivate Ctrl
User->>Ctrl: PATCH /job/:id/publish (CompleteJobDto)
activate Ctrl
Ctrl->>Guard: canActivate(jobId, userId)
activate Guard
Guard->>Repo: findOneById(jobId)
activate Repo
Repo->>DB: SELECT * FROM tb_jobs WHERE id=:id
DB-->>Repo: JobsEntity {jobStatus=DRAFT, company_id}
Repo-->>Guard: JobsEntity
deactivate Repo
Guard->>Guard: Validar: userId === job.company_id
Guard-->>Ctrl: true
deactivate Guard
Ctrl->>Service: PublishJobService.execute(jobId, completeData)
activate Service
Service->>Repo: publishJob(jobId, completeData)
activate Repo
Repo->>DB: UPDATE tb_jobs SET jobStatus=PUBLISHED, <br/>description, requirements, ...
DB-->>Repo: JobsEntity {jobStatus=PUBLISHED, publishedAt}
Repo-->>Service: JobsEntity
deactivate Repo
Service-->>Ctrl: {id, status=PUBLISHED, publishedAt, message}
deactivate Service
Ctrl-->>User: 200 OK
deactivate Ctrl
User->>Ctrl: PATCH /job/:id/cancel
activate Ctrl
Ctrl->>Guard: canActivate(jobId, userId)
activate Guard
Guard->>Repo: findOneById(jobId)
activate Repo
Repo->>DB: SELECT * FROM tb_jobs WHERE id=:id
DB-->>Repo: JobsEntity {jobStatus=PUBLISHED}
Repo-->>Guard: JobsEntity
deactivate Repo
Guard-->>Ctrl: true
deactivate Guard
Ctrl->>Service: CancelJobService.execute(jobId)
activate Service
Service->>Repo: cancelJob(jobId)
activate Repo
Repo->>DB: UPDATE tb_jobs SET jobStatus=CANCELED, canceledAt=NOW()
DB-->>Repo: JobsEntity {jobStatus=CANCELED}
Repo-->>Service: JobsEntity
deactivate Repo
Service-->>Ctrl: {id, status=CANCELED, canceledAt, message}
deactivate Service
Ctrl-->>User: 200 OK
deactivate Ctrl
Note over Guard,DB: Guard bloqueia acesso se:<br/>- Usuário não autenticado<br/>- Vaga não encontrada<br/>- Usuário não é proprietário
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutos Áreas de atenção especial:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/modules/jobs/repository/job.repository.ts (1)
164-168: Vulnerabilidade de SQL Injection.A interpolação direta de
searchQueryna consulta SQL cria uma vulnerabilidade crítica de SQL injection. Utilize parâmetros nomeados para sanitizar a entrada.queryBuilder .leftJoin('job.company', 'company') .select(['job', 'company.id', 'company.companyName', 'company.profile']) - .andWhere(`job.title ILIKE '%${searchQuery}%'`) + .andWhere(`job.title ILIKE :searchQuery`, { searchQuery: `%${searchQuery}%` }) .andWhere(`job.jobStatus = :status`, { status: JobStatus.PUBLISHED })
🧹 Nitpick comments (16)
src/modules/mails/mail.module.ts (1)
13-21: Porta do SMTP como número e clarificação da authAjuste para usar Mailhog sem autenticação faz sentido. Só sugiro converter explicitamente a porta para número para evitar qualquer surpresa de tipo entre ambientes.
- host: config.get('MAIL_HOST'), - port: config.get('MAIL_PORT'), + host: config.get('MAIL_HOST'), + port: Number(config.get('MAIL_PORT')),src/shared/Swagger/decorators/jobs/publish-job.swagger.ts (1)
1-30: Swagger de publicação bem alinhado com erros esperadosO decorator documenta bem o fluxo de publicação (OK + 400/401/403) e reaproveita os modelos de erro existentes.
Se a resposta
403tiver um corpo padronizado na API (similar aoUnauthorizedSwagger), pode valer a pena no futuro extrair umForbiddenSwaggerpara manter a consistência dos contratos.test/modules/jobs/services/job-owner.guard.spec.ts (1)
1-101: Testes do JobOwnerGuard bem completos, com pequeno ajuste possívelA suíte cobre bem todos os cenários relevantes do guard (não autenticado, job inexistente, usuário não dono e dono).
Em vários testes você chama
guard.canActivate(context)duas vezes apenas para validar tipo e depois mensagem da exceção. Se o método ganhar efeitos colaterais no futuro, isso pode virar fonte de flakiness. Opcionalmente, dá para capturar o erro uma única vez (viatry/catchouawait guard.canActivate(context).catch(...)) e fazer múltiplosexpectsobre o mesmo objeto.test/modules/jobs/services/publish-job.service.spec.ts (1)
1-102: Cobertura de PublishJobService está boa; dá para enxugar chamadas duplicadasOs testes cobrem bem os casos de erro (job inexistente / não-draft) e o fluxo feliz, incluindo o payload retornado e a chamada correta a
publishJob.Nos casos de erro, você chama
service.executeduas vezes no mesmo teste para checar tipo e mensagem daBadRequestException. Assim como no guard, é mais robusto capturar a exceção uma única vez e fazer múltiplos asserts sobre ela, evitando executar a lógica de serviço duas vezes sem necessidade.src/shared/Swagger/decorators/jobs/delete-job-draft.swagger.ts (1)
1-30: Swagger de exclusão de rascunho consistente com o restante da APIAs respostas documentadas (OK, 400, 401, 403) e o summary descrevem bem o comportamento esperado do endpoint, mantendo o padrão dos outros decorators de vagas.
Assim como no caso de publicação, se a resposta
403tiver um corpo padronizado, pode ser interessante extrair um modelo (ex.:ForbiddenSwagger) para manter os contratos de erro homogêneos.test/modules/jobs/services/create-job-draft.service.spec.ts (2)
49-62: Considere consolidar as asserções de exceção.Chamar
service.execute()duas vezes para verificar a exceção é redundante e pode afetar a performance dos testes. Utilizerejects.toThrow()com um matcher mais específico.- await expect(service.execute(invalidData, mockCompany)).rejects.toThrow( - BadRequestException, - ); - await expect(service.execute(invalidData, mockCompany)).rejects.toThrow( - 'Salary minimum cannot be greater than salary maximum', - ); + await expect(service.execute(invalidData, mockCompany)).rejects.toThrow( + new BadRequestException('Salary minimum cannot be greater than salary maximum'), + );
87-104: Teste parcialmente redundante com o anterior.Este teste valida a associação do
company_id, mas essa verificação já é coberta implicitamente no teste anterior (linhas 81-84). Considere manter apenas um teste mais abrangente ou diferenciar os cenários testados.src/modules/jobs/services/create-job-draft.service.ts (1)
19-19: Evite mutar o parâmetro de entrada diretamente.Atribuir
company_iddiretamente ao objetodataviola o princípio de imutabilidade e pode causar efeitos colaterais inesperados. Prefira criar um novo objeto para passar ao repositório.- data.company_id = company.id; - - const draft = await this.jobRepository.createJobDraft(data); + const draftData = { + ...data, + company_id: company.id, + }; + + const draft = await this.jobRepository.createJobDraft(draftData);src/modules/jobs/guards/job-owner.guard.ts (1)
23-27: Guard realiza busca no banco que pode ser duplicada no serviço.O guard busca a vaga para verificar ownership, mas os serviços (
DeleteJobDraftService,CancelJobService,PublishJobService) também buscam a mesma vaga novamente. Considere armazenar a vaga no request para evitar duplicação de queries.const job = await this.jobRepository.findOneById(jobId); if (!job) { throw new NotFoundException('Job not found'); } + // Anexa a vaga ao request para reutilização nos serviços + request.job = job; + if (job.company_id !== user.id) {src/modules/jobs/services/delete-job-draft.service.ts (1)
9-14: UsarNotFoundExceptionpara recurso não encontrado.O uso de
BadRequestExceptionpara "Job not found" é semanticamente incorreto. O código HTTP 400 indica erro do cliente na requisição, enquanto 404 indica recurso não encontrado. Além disso, considere adicionar a anotação de tipo de retorno no método.+import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; -import { BadRequestException, Injectable } from '@nestjs/common';- async execute(jobId: string) { + async execute(jobId: string): Promise<{ message: string }> { const job = await this.jobRepository.findOneById(jobId); if (!job) { - throw new BadRequestException('Job not found'); + throw new NotFoundException('Job not found'); }src/modules/jobs/services/cancel-job.service.ts (1)
9-14: Consistência: usarNotFoundExceptionpara recurso não encontrado.Mesmo padrão identificado em
DeleteJobDraftService. UtilizeNotFoundExceptionem vez deBadRequestExceptionquando o job não for encontrado, mantendo consistência semântica dos códigos HTTP.-import { BadRequestException, Injectable } from '@nestjs/common'; +import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';if (!job) { - throw new BadRequestException('Job not found'); + throw new NotFoundException('Job not found'); }src/modules/jobs/dtos/create-job-draft.dto.ts (2)
50-57: Validação incompleta paracityquando modalidade não é remota.O
@ValidateIfcorretamente condiciona a validação, mas falta@IsNotEmpty()para garantir quecityseja obrigatório quando a modalidade não for REMOTE. Atualmente, uma string vazia seria aceita.@ValidateIf((o) => o.modality !== JobsModalityEnum.REMOTE) + @IsNotEmpty() @IsString() @ApiProperty({ required: false, description: 'Cidade', example: 'São Paulo', }) city?: string;
90-91: Adicionar validação de tipo paracompany_id.Embora
company_idseja definido pelo serviço, adicionar@IsString()previne erros caso o campo seja inadvertidamente passado no payload com tipo incorreto.@IsOptional() + @IsString() company_id?: string;src/modules/jobs/dtos/complete-job.dto.ts (1)
23-31: Considerar validação de tamanho mínimo para arrays obrigatórios.Os campos
requirementseselectionProcesssão obrigatórios, mas arrays vazios passariam na validação atual. Considere adicionar@ArrayMinSize(1)para garantir pelo menos um elemento.+import { ArrayMinSize } from 'class-validator'; @IsNotEmpty() @IsArray() + @ArrayMinSize(1) @IsString({ each: true }) @ApiProperty({ required: true, description: 'Requisitos da vaga', example: ['Node.js', 'TypeScript', 'NestJS'], }) requirements: string[];src/modules/jobs/jobs.controller.ts (2)
98-104: Considere tornar a rota mais explícita.O nome do método
deleteJobDraftsugere que é específico para rascunhos, mas a rota@Delete(':id')é genérica e conflita com possíveis outros endpoints DELETE. Se o serviço valida internamente que apenas rascunhos podem ser excluídos, considere adicionar essa validação no Swagger ou renomear o método para refletir melhor o comportamento real.
135-140: Evite usar@Res()a menos que seja estritamente necessário.O uso do decorador
@Res()desabilita o tratamento automático de respostas do NestJS, exigindo gerenciamento manual. A menos que você precise de acesso de baixo nível ao objeto de resposta (por exemplo, para streaming ou manipulação de headers específicos), é melhor deixar o framework lidar com a resposta automaticamente retornando o objeto diretamente.Considere esta refatoração:
- @Res() res: Response, ) { - const { status, data } = await this.getAllJobsFromLoggedCompany.execute( + const result = await this.getAllJobsFromLoggedCompany.execute( company.id, ); - return res.status(status).json(data); + return result; }Se o status HTTP precisa ser customizado, use
@HttpCode()ou lance exceções apropriadas.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
docker-compose.yml(1 hunks)src/database/entities/jobs.entity.ts(6 hunks)src/database/migrations/1765483583757-AddJobStatusFields.ts(1 hunks)src/modules/jobs/dtos/complete-job.dto.ts(1 hunks)src/modules/jobs/dtos/create-job-draft.dto.ts(1 hunks)src/modules/jobs/enums/job-status.enum.ts(1 hunks)src/modules/jobs/guards/job-owner.guard.ts(1 hunks)src/modules/jobs/jobs.controller.ts(5 hunks)src/modules/jobs/jobs.module.ts(2 hunks)src/modules/jobs/repository/job.repository.ts(5 hunks)src/modules/jobs/services/cancel-job.service.ts(1 hunks)src/modules/jobs/services/create-job-draft.service.ts(1 hunks)src/modules/jobs/services/delete-job-draft.service.ts(1 hunks)src/modules/jobs/services/publish-job.service.ts(1 hunks)src/modules/mails/mail.module.ts(1 hunks)src/shared/Swagger/decorators/jobs/cancel-job.swagger.ts(1 hunks)src/shared/Swagger/decorators/jobs/create-job-draft.swagger.ts(1 hunks)src/shared/Swagger/decorators/jobs/delete-job-draft.swagger.ts(1 hunks)src/shared/Swagger/decorators/jobs/publish-job.swagger.ts(1 hunks)test/modules/jobs/services/create-job-draft.service.spec.ts(1 hunks)test/modules/jobs/services/job-owner.guard.spec.ts(1 hunks)test/modules/jobs/services/publish-job.service.spec.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
src/shared/Swagger/decorators/jobs/delete-job-draft.swagger.ts (2)
src/shared/Swagger/bad-request.swagger.ts (1)
BadRequestSwagger(2-5)src/shared/Swagger/unauthorized.swagger.ts (1)
UnauthorizedSwagger(2-8)
src/modules/jobs/services/create-job-draft.service.ts (1)
src/modules/jobs/dtos/create-job-draft.dto.ts (1)
CreateJobDraftDto(15-92)
src/modules/jobs/services/cancel-job.service.ts (5)
src/modules/jobs/guards/job-owner.guard.ts (1)
Injectable(10-37)src/modules/jobs/repository/job.repository.ts (1)
Injectable(18-196)src/modules/jobs/services/create-job-draft.service.ts (1)
Injectable(6-29)src/modules/jobs/services/delete-job-draft.service.ts (1)
Injectable(5-26)src/modules/jobs/services/publish-job.service.ts (1)
Injectable(6-33)
src/shared/Swagger/decorators/jobs/publish-job.swagger.ts (2)
src/shared/Swagger/bad-request.swagger.ts (1)
BadRequestSwagger(2-5)src/shared/Swagger/unauthorized.swagger.ts (1)
UnauthorizedSwagger(2-8)
src/shared/Swagger/decorators/jobs/cancel-job.swagger.ts (2)
src/shared/Swagger/bad-request.swagger.ts (1)
BadRequestSwagger(2-5)src/shared/Swagger/unauthorized.swagger.ts (1)
UnauthorizedSwagger(2-8)
src/shared/Swagger/decorators/jobs/create-job-draft.swagger.ts (2)
src/shared/Swagger/bad-request.swagger.ts (1)
BadRequestSwagger(2-5)src/shared/Swagger/unauthorized.swagger.ts (1)
UnauthorizedSwagger(2-8)
src/modules/jobs/guards/job-owner.guard.ts (5)
src/modules/jobs/repository/job.repository.ts (1)
Injectable(18-196)src/modules/jobs/services/cancel-job.service.ts (1)
Injectable(5-29)src/modules/jobs/services/create-job-draft.service.ts (1)
Injectable(6-29)src/modules/jobs/services/delete-job-draft.service.ts (1)
Injectable(5-26)src/modules/jobs/services/publish-job.service.ts (1)
Injectable(6-33)
src/modules/jobs/repository/job.repository.ts (8)
src/modules/jobs/services/cancel-job.service.ts (1)
Injectable(5-29)src/modules/jobs/services/create-job-draft.service.ts (1)
Injectable(6-29)src/modules/jobs/services/delete-job.service.ts (1)
Injectable(7-36)src/modules/jobs/services/get-all-jobs.service.ts (1)
Injectable(7-19)src/modules/jobs/services/update-job.service.ts (1)
Injectable(5-22)src/shared/utils/handle-error.util.ts (1)
handleError(3-14)src/modules/jobs/dtos/create-job-draft.dto.ts (1)
CreateJobDraftDto(15-92)src/modules/jobs/dtos/complete-job.dto.ts (1)
CompleteJobDto(12-85)
src/modules/jobs/jobs.controller.ts (8)
src/shared/Swagger/decorators/jobs/create-job-draft.swagger.ts (1)
CreateJobDraftSwagger(6-26)src/modules/jobs/dtos/create-job-draft.dto.ts (1)
CreateJobDraftDto(15-92)src/modules/auth/decorator/logged-company.decorator.ts (1)
LoggedCompany(7-20)src/shared/Swagger/decorators/jobs/publish-job.swagger.ts (1)
PublishJobSwagger(6-30)src/modules/jobs/dtos/complete-job.dto.ts (1)
CompleteJobDto(12-85)src/shared/Swagger/decorators/jobs/cancel-job.swagger.ts (1)
CancelJobSwagger(6-30)src/shared/Swagger/decorators/jobs/delete-job-draft.swagger.ts (1)
DeleteJobDraftSwagger(6-30)src/shared/Swagger/decorators/jobs/get-all-jobs-of-logged-company.swagger.ts (1)
GetAllJobsOfLoggedCompanySwagger(4-10)
🔇 Additional comments (25)
src/shared/Swagger/decorators/jobs/create-job-draft.swagger.ts (1)
1-26: Swagger de criação de rascunho bem padronizadoDecorator está consistente com o padrão do projeto (status 201/400/401 + summary claro) e reaproveita os modelos de erro compartilhados. Nada a ajustar aqui.
src/modules/jobs/enums/job-status.enum.ts (1)
1-6: Enum JobStatus bem definido para o ciclo de vida das vagasOs valores cobrem bem o fluxo de rascunho/publicação/arquivamento/cancelamento e o uso de strings facilita integração com banco e Swagger. Tudo ok aqui.
test/modules/jobs/services/create-job-draft.service.spec.ts (2)
8-10: LGTM!A classe mock é simples e suficiente para os testes. Boa prática ao manter o mock focado apenas no método necessário.
64-85: LGTM!O teste verifica corretamente a estrutura de retorno e a chamada ao repositório com os parâmetros esperados.
src/modules/jobs/guards/job-owner.guard.ts (1)
14-21: LGTM!Boa prática verificar autenticação antes de fazer chamadas ao banco de dados. A ordem das validações está correta.
src/modules/jobs/services/publish-job.service.ts (1)
21-31: LGTM!A lógica de publicação está correta. O retorno inclui informações relevantes como
id,status,publishedAte uma mensagem de sucesso.src/modules/jobs/services/create-job-draft.service.ts (1)
10-17: A validação deundefined/nulljá é garantida pelo DTO.Os campos
salaryMinesalaryMaxemCreateJobDraftDtopossuem os decoradores@IsNotEmpty()e@IsNumber()(linhas 59-68 e 70-79), que garantem que ambos os valores são sempre números válidos quando chegam ao serviço. A comparaçãosalaryMin > salaryMaxé segura e não há risco de valoresundefinedounull.src/modules/jobs/jobs.module.ts (1)
20-48: LGTM!Os novos serviços e o guard foram corretamente importados e registrados como providers no módulo. A estrutura segue as convenções do NestJS.
src/modules/jobs/services/cancel-job.service.ts (1)
16-27: LGTM!A lógica de validação do status e a estrutura do retorno estão corretas. A verificação de que apenas jobs publicados podem ser cancelados está adequada.
src/modules/jobs/repository/job.repository.ts (2)
30-37: LGTM!O método
createJobDraftestá bem implementado, criando o draft com status DRAFT e mapeandointerestAreaparaprerequisitesconforme a lógica de negócio.
60-73: LGTM!Os métodos
cancelJobedeleteJobDraftestão implementados corretamente, atualizando o status e registrando a data de cancelamento quando aplicável.src/modules/jobs/dtos/complete-job.dto.ts (1)
43-64: LGTM!As validações para
contractTypeejourneyestão corretas, com uso adequado de enum e decoradores do Swagger.src/database/migrations/1765483583757-AddJobStatusFields.ts (3)
7-10: LGTM!A tabela
tb_saved_jobsestá bem estruturada com índices apropriados parauserIdejobId, otimizando consultas de jobs salvos por usuário ou por vaga.
11-23: LGTM!A adição do enum
jobStatuse dos campos temporais (publishedAt,canceledAt) está correta. A alteração das colunas para nullable suporta adequadamente o fluxo de draft onde esses campos são preenchidos posteriormente.
43-78: Verificar consistência dodownpara FK detb_alerts.O método
updropa a FK detb_alerts(linha 7) e recria comON DELETE NO ACTION(linha 40), mas odownrecria comON DELETE CASCADE(linha 77). Isso pode resultar em comportamento diferente após rollback. Verifique qual era o comportamento original pretendido.src/modules/jobs/jobs.controller.ts (5)
39-49: LGTM! Importações organizadas corretamente.As novas importações para DTOs, serviços, guards e decoradores Swagger estão bem estruturadas e são necessárias para a funcionalidade de rascunhos.
62-65: LGTM! Injeção de dependências correta.Os novos serviços estão sendo injetados adequadamente no construtor do controller.
68-77: LGTM! Endpoint de criação de rascunho bem implementado.O endpoint está protegido com autenticação, usa os DTOs corretos e delega a lógica para o serviço apropriado.
79-88: LGTM! Endpoint de publicação bem implementado.O endpoint utiliza corretamente o
JobOwnerGuardpara garantir que apenas o proprietário possa publicar a vaga.
90-96: LGTM! Endpoint de cancelamento bem implementado.A proteção com
JobOwnerGuardgarante que apenas o proprietário possa cancelar a vaga.src/database/entities/jobs.entity.ts (5)
15-15: LGTM! Importação necessária adicionada.O enum
JobStatusé necessário para o novo campo de controle de estado das vagas.
28-32: LGTM! Campos opcionais para rascunhos.Tornar
descriptioneprerequisitesanuláveis é consistente com o sistema de rascunhos, onde esses campos podem ser preenchidos posteriormente.
45-58: LGTM! Campos de tipo agora opcionais para rascunhos.A anulabilidade de
typeetypeContractpermite criar rascunhos sem especificar esses campos inicialmente.
83-93: LGTM! Campos de contrato flexibilizados.Tornar
openEndedContracteaffirmativeanuláveis fornece mais flexibilidade no preenchimento de rascunhos.
108-110: LGTM! Formatação do relacionamento atualizada.A mudança de aspas duplas para aspas simples é uma melhoria de consistência de estilo.
| database_soujunior: | ||
| image: postgres | ||
| container_name: database_soujunior | ||
| container_name: vagas-api-db | ||
| restart: always | ||
| ports: | ||
| - 5432:5432 | ||
| - '5432:5432' | ||
| environment: | ||
| - POSTGRES_USER=docker | ||
| - POSTGRES_PASSWORD=ignite | ||
| - POSTGRES_DB=linkedin_backend | ||
| - POSTGRES_USER=${TYPEORM_USERNAME} | ||
| - POSTGRES_PASSWORD=${TYPEORM_PASSWORD} | ||
| - POSTGRES_DB=${TYPEORM_DATABASE} | ||
| volumes: | ||
| - pgdata:/data/postgres | ||
| healthcheck: | ||
| test: ["CMD-SHELL", "pg_isready"] | ||
| test: ['CMD-SHELL', 'pg_isready -U ${TYPEORM_USERNAME}'] | ||
| interval: 10s | ||
| timeout: 5s | ||
| retries: 5 | ||
|
|
||
| mailhog: | ||
| image: mailhog/mailhog | ||
| container_name: mailhog | ||
| restart: always | ||
| ports: | ||
| - '1025:1025' | ||
| - '8025:8025' | ||
|
|
||
| app: | ||
| build: . | ||
| container_name: linkedIn_Backend | ||
| container_name: linkedin-backend | ||
| restart: always | ||
| ports: | ||
| - 3000:3000 | ||
| - '3001:3001' | ||
| volumes: | ||
| - .:/user/app | ||
| - .:/usr/app | ||
| depends_on: | ||
| database_soujunior: | ||
| condition: service_healthy | ||
| mailhog: | ||
| condition: service_started |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Procurar por configurações de porta em arquivos JavaScript/TypeScript
rg -i "listen|port" --type ts --type js -n | head -50Repository: SouJunior/vagas-api
Length of output: 6107
🏁 Script executed:
# Verificar o Dockerfile
find . -name "Dockerfile" -type f | head -5Repository: SouJunior/vagas-api
Length of output: 75
🏁 Script executed:
# Procurar por ambiente PORT ou 3001/3000
rg "3001|3000|PORT" --type ts --type js --type yml -n | head -50Repository: SouJunior/vagas-api
Length of output: 90
🏁 Script executed:
# Read the Dockerfile
cat -n ./DockerfileRepository: SouJunior/vagas-api
Length of output: 541
🏁 Script executed:
# Look for main.ts where the app starts
find . -name "main.ts" -type fRepository: SouJunior/vagas-api
Length of output: 76
🏁 Script executed:
# Search for port configuration more broadly
rg "3001|3000|\.listen" -n | head -30Repository: SouJunior/vagas-api
Length of output: 34420
🏁 Script executed:
# Check package.json for startup command
cat -n package.jsonRepository: SouJunior/vagas-api
Length of output: 4221
Corrigir porta da aplicação e WORKDIR no Dockerfile com urgência
Existem dois problemas críticos que impedem o container de funcionar corretamente:
-
Mismatch de porta: O docker-compose mapeia
3001:3001, mas a aplicação NestJS escuta na porta3000por padrão (src/main.tslinha 40). Sem definir a variávelPORTnoenvironmentdo docker-compose, o mapeamento fica inoperante. Adicione- PORT=3001ao serviçoappou ajuste o mapeamento para3000:3000. -
WORKDIR incorreto no Dockerfile: Linha 3 define
WORKDIR /user/app(deve ser/usr/app). Esse typo causa falhas no build e no volume bind. Corrija paraWORKDIR /usr/appem todas as ocorrências (linhas 3, 15-17).
🤖 Prompt for AI Agents
In docker-compose.yml around lines 4 to 42: fix two issues—(1) the app service
port mismatch: either add the environment entry "- PORT=3001" under the app
service or change the ports mapping to "3000:3000" so the mapped host port
matches the NestJS app listening port; (2) correct the WORKDIR typo in the
project's Dockerfile (currently "WORKDIR /user/app") to "WORKDIR /usr/app" in
every occurrence (mentioned at lines ~3 and ~15-17 of the Dockerfile) so builds
and volume binds use the correct path.
|
|
||
| @Column({ nullable: true }) | ||
| contractText?: string; | ||
| contractText: string; |
There was a problem hiding this comment.
Inconsistência entre tipo TypeScript e schema do banco de dados.
O campo contractText foi alterado de opcional (contractText?: string) para obrigatório (contractText: string) no TypeScript, mas a coluna permanece nullable: true no banco. Isso cria uma discrepância onde o TypeScript exige o campo, mas o banco permite valores nulos.
Escolha uma das soluções:
Opção 1: Se o campo deve ser opcional (recomendado para rascunhos):
-contractText: string;
+contractText?: string;Opção 2: Se o campo deve ser obrigatório:
-@Column({ nullable: true })
+@Column({ nullable: false })
contractText: string;📝 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.
| contractText: string; | |
| contractText?: string; |
🤖 Prompt for AI Agents
In src/database/entities/jobs.entity.ts around line 90, the TypeScript property
was changed from optional to required (contractText: string) while the DB column
is still nullable, causing a type/schema mismatch; either make the TS property
optional again (contractText?: string) to match nullable: true for drafts, or
make the DB column non-nullable (remove nullable: true / set nullable: false and
add a migration to set existing nulls to '' or a default) so the schema enforces
the required field—pick the correct option and update both the entity and
database migration accordingly.
| @Column({ nullable: false, default: 'ACTIVE' }) | ||
| status: string; | ||
|
|
||
| @Column({ | ||
| type: 'enum', | ||
| enum: JobStatus, | ||
| default: JobStatus.DRAFT, | ||
| }) | ||
| jobStatus: JobStatus; | ||
|
|
||
| @Column({ nullable: true }) | ||
| publishedAt: Date; | ||
|
|
||
| @Column({ nullable: true }) | ||
| canceledAt: Date; |
There was a problem hiding this comment.
Dois campos de status podem causar confusão e bugs.
A entidade agora possui dois campos para controlar o estado:
status(linha 115-116): string com valor padrão 'ACTIVE'jobStatus(linhas 118-123): enum JobStatus com valor padrão DRAFT
Ter dois campos de status pode levar a estados inconsistentes e dificultar a manutenção. Por exemplo, uma vaga pode estar com status: 'ACTIVE' mas jobStatus: DRAFT, criando ambiguidade.
Recomendações:
- Se
statusé usado para um propósito diferente (ex: soft delete com ACTIVE/ARCHIVED), considere renomeá-lo para deixar mais claro (ex:archivalStatusourecordStatus) - Documente claramente a diferença entre os dois campos
- Considere consolidar em um único campo de status se possível
- Adicione validação para evitar estados inconsistentes
Você gostaria que eu gerasse um script para verificar como status é usado no restante do código?
🤖 Prompt for AI Agents
In src/database/entities/jobs.entity.ts lines 115-129 there are two overlapping
status fields (status: string default 'ACTIVE' and jobStatus: JobStatus enum
default DRAFT) which can produce inconsistent state; decide on a single source
of truth: if the string field represents record lifecycle (soft-delete/archival)
rename it to a clearer name (e.g., archivalStatus or recordStatus) and update
the DB column and DTOs, otherwise consolidate both into the JobStatus enum;
update all usages across the codebase (search and replace) and database
migrations accordingly; add runtime validation (entity hooks or class-validator)
to prevent contradictory values and add a brief comment/JSDoc on the chosen
field explaining its purpose to avoid future confusion.
| @Column({ | ||
| type: 'enum', | ||
| enum: JobStatus, | ||
| default: JobStatus.DRAFT, | ||
| }) | ||
| jobStatus: JobStatus; | ||
|
|
||
| @Column({ nullable: true }) | ||
| publishedAt: Date; | ||
|
|
||
| @Column({ nullable: true }) | ||
| canceledAt: Date; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "*.ts" | grep -i "publish" | grep -i "service" | head -20Repository: SouJunior/vagas-api
Length of output: 170
🏁 Script executed:
cat -n ./src/modules/jobs/services/publish-job.service.tsRepository: SouJunior/vagas-api
Length of output: 1249
🏁 Script executed:
# Find and read the CompleteJobDto
find . -type f -name "*complete-job.dto*" | head -5Repository: SouJunior/vagas-api
Length of output: 106
🏁 Script executed:
cat -n ./src/modules/jobs/dtos/complete-job.dto.tsRepository: SouJunior/vagas-api
Length of output: 2471
🏁 Script executed:
# Search for controllers that use PublishJobService
rg -B 5 -A 10 "PublishJobService" --type=ts -g '!*.spec.ts' | grep -A 10 "@Controller\|@Post\|@Put\|execute"Repository: SouJunior/vagas-api
Length of output: 1445
🏁 Script executed:
# Read the jobs controller to find the publish endpoint
cat -n ./src/modules/jobs/jobs.controller.ts | grep -A 20 -B 2 "publish"Repository: SouJunior/vagas-api
Length of output: 3064
🏁 Script executed:
# Check for ValidationPipe setup
find . -name "main.ts" -o -name "app.module.ts" | head -5Repository: SouJunior/vagas-api
Length of output: 96
🏁 Script executed:
cat -n ./src/main.tsRepository: SouJunior/vagas-api
Length of output: 1588
🏁 Script executed:
# Find and read the job repository
find . -type f -name "*job.repository*" | grep -v specRepository: SouJunior/vagas-api
Length of output: 110
🏁 Script executed:
cat -n ./src/modules/jobs/repository/job.repository.tsRepository: SouJunior/vagas-api
Length of output: 7671
🏁 Script executed:
cat -n ./src/database/entities/jobs.entity.tsRepository: SouJunior/vagas-api
Length of output: 4346
🏁 Script executed:
# Check CreateJobDraftDto
cat -n ./src/modules/jobs/dtos/create-job-draft.dto.tsRepository: SouJunior/vagas-api
Length of output: 2475
Validação de campos obrigatórios na publicação está parcialmente implementada, mas sem lógica explícita no serviço.
A validação ocorre no nível do DTO (CompleteJobDto) via decoradores @IsNotEmpty e o ValidationPipe global, garantindo que description, requirements, contractType, journey e selectionProcess sejam fornecidos. No entanto, PublishJobService.execute() não contém lógica explícita de validação — depende integralmente do pipeline de validação do NestJS. Para maior robustez e clareza, considere adicionar validações explícitas no PublishJobService, especialmente para cenários onde o repositório possa ser chamado diretamente, ou garantir que toda publicação passe obrigatoriamente pela validação do DTO.
🤖 Prompt for AI Agents
In src/database/entities/jobs.entity.ts around lines 118-129, the entity marks
publishedAt and canceledAt nullable and DTO-level validation exists but
PublishJobService.execute() lacks explicit checks; update
PublishJobService.execute to explicitly validate required publication fields
(description, requirements, contractType, journey, selectionProcess) before
persisting: fetch the job, verify each required field is present and non-empty,
throw a proper HTTP/Domain error (e.g., BadRequest) when any are missing, set
jobStatus to PUBLISHED and publishedAt to now, then save via the repository;
this ensures robustness if the service is called bypassing the global
ValidationPipe.
| 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`); | ||
| 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`); |
There was a problem hiding this comment.
Revisar comportamento ON DELETE para constraint de jobId em tb_saved_jobs (linha 37).
A FK para jobId usa ON DELETE NO ACTION, o que impedirá a exclusão de um job enquanto existirem referências em tb_saved_jobs. Isso é inconsistente com a FK para userId na mesma tabela (linha 36), que usa ON DELETE CASCADE. Considere usar ON DELETE CASCADE para permitir limpeza automática de saved_jobs ao deletar um job, ou ON DELETE SET NULL se preferir manter o histórico com jobId nulo.
🤖 Prompt for AI Agents
In src/database/migrations/1765483583757-AddJobStatusFields.ts around lines
36–37, the foreign key constraint for tb_saved_jobs.jobId is created with ON
DELETE NO ACTION which prevents deleting a job while saved_job rows exist;
change the constraint to ON DELETE CASCADE to match the userId behavior (or, if
the design requires preserving saved job records, change to ON DELETE SET NULL
and make the jobId column nullable) and update the migration SQL accordingly so
saved_job rows are cleaned up or preserved as intended.
| if (job.company_id !== user.id) { | ||
| throw new ForbiddenException( | ||
| 'You do not have permission to modify this job', | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, examine the job-owner.guard.ts file to understand the context
cat -n src/modules/jobs/guards/job-owner.guard.tsRepository: SouJunior/vagas-api
Length of output: 1230
🏁 Script executed:
# Search for request.user patterns to understand user structure
rg -n "request\.user" --type ts -A 2 -B 2Repository: SouJunior/vagas-api
Length of output: 1854
🏁 Script executed:
# Search for authentication strategy and user setup
rg -n "strategy|AuthGuard|auth" --type ts src/modules -A 2 | head -100Repository: SouJunior/vagas-api
Length of output: 6362
🏁 Script executed:
# Check for company_id property usage on user object
rg -n "user\.company_id|user\.id" --type ts -B 1 -A 1Repository: SouJunior/vagas-api
Length of output: 2577
🏁 Script executed:
# Search for user entity/interface definitions
rg -n "interface User|class User|type User" --type ts -A 5Repository: SouJunior/vagas-api
Length of output: 6548
🏁 Script executed:
# Check LoggedCompany decorator to understand its structure
cat -n src/modules/auth/decorator/logged-company.decorator.tsRepository: SouJunior/vagas-api
Length of output: 656
🏁 Script executed:
# Check LoggedUser decorator for comparison
cat -n src/modules/auth/decorator/logged-user.decorator.tsRepository: SouJunior/vagas-api
Length of output: 613
🏁 Script executed:
# Check JWT strategy to see what gets attached to request.user
cat -n src/modules/auth/jtw/jwt.strategy.tsRepository: SouJunior/vagas-api
Length of output: 1625
🏁 Script executed:
# Check Job entity structure to understand company_id
fd job.entity.ts -type f -exec cat -n {} \;Repository: SouJunior/vagas-api
Length of output: 233
🏁 Script executed:
# Check how jobs controller uses these decorators
rg -n "@LoggedCompany|@LoggedUser" src/modules/jobs -B 2 -A 5Repository: SouJunior/vagas-api
Length of output: 1645
🏁 Script executed:
# Find and check CompaniesEntity structure
find . -name "*compan*entity*" -type f | head -20Repository: SouJunior/vagas-api
Length of output: 106
🏁 Script executed:
# Search for CompaniesEntity definition
rg -n "class CompaniesEntity|export class CompaniesEntity" --type ts -A 10Repository: SouJunior/vagas-api
Length of output: 715
🏁 Script executed:
# Check if there are any middleware or guards that ensure only companies access job endpoints
rg -n "publishJob|deleteJobDraft|cancelJob" src/modules/jobs/jobs.controller.ts -B 5 -A 2Repository: SouJunior/vagas-api
Length of output: 1312
🏁 Script executed:
# Final verification: Check if job endpoints should only be accessible by companies
# by looking at how jobs are created to understand the expected user type
cat -n src/modules/jobs/jobs.controller.ts | grep -A 10 "createJobDraft\|createNewJob"Repository: SouJunior/vagas-api
Length of output: 1279
🏁 Script executed:
# Check if there's any role validation or company check elsewhere
rg -n "isCompany|role|company" src/modules/auth/decorator --type ts -A 3Repository: SouJunior/vagas-api
Length of output: 45
A comparação job.company_id !== user.id possui um flaw semântico crítico.
O guarda assume que request.user.id representa uma empresa, mas a estratégia JWT (jwt.strategy.ts) pode retornar tanto UserEntity quanto CompaniesEntity. Como os endpoints de modificação de job (publishJob, cancelJob, deleteJobDraft) usam apenas @UseGuards(AuthGuard()) sem a validação explícita @LoggedCompany() presente nos endpoints de criação, um usuário comum autenticado poderia chegar até o guarda, causando falha na comparação ou comportamento de segurança incorreto.
Solução necessária: Adicione @LoggedCompany() aos endpoints de modificação de job (linhas 83, 94, 102) para garantir que apenas empresas acessem estes endpoints, alinhando com o padrão usado em createJobDraft e createNewJob.
🤖 Prompt for AI Agents
In src/modules/jobs/guards/job-owner.guard.ts around lines 29 to 33 the guard
compares job.company_id !== user.id but this is unsafe because request.user may
be a UserEntity or CompaniesEntity; update the endpoints that modify jobs to
ensure request.user is always a CompaniesEntity by adding the @LoggedCompany()
decorator to the publishJob, cancelJob, and deleteJobDraft controller methods
(around lines 83, 94, 102) so only companies can reach this guard and the id
comparison is semantically correct.
| jobId: string, | ||
| @Body('content') content: string, | ||
| ) { | ||
| async archivedJob(@Param() jobId: string, @Body('content') content: string) { |
There was a problem hiding this comment.
Corrigir bug no decorador de parâmetro.
O decorador @Param() sem argumentos retorna o objeto inteiro de parâmetros, não apenas o id. Isso provavelmente causará um erro no serviço que espera uma string.
Aplique esta correção:
-async archivedJob(@Param() jobId: string, @Body('content') content: string) {
+async archivedJob(@Param('id') jobId: string, @Body('content') content: string) {
return this.deleteJobService.execute(jobId, content);
}📝 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.
| async archivedJob(@Param() jobId: string, @Body('content') content: string) { | |
| async archivedJob(@Param('id') jobId: string, @Body('content') content: string) { | |
| return this.deleteJobService.execute(jobId, content); | |
| } |
🤖 Prompt for AI Agents
src/modules/jobs/jobs.controller.ts around line 159: o decorador @Param() está
retornando o objeto completo de parâmetros em vez de apenas o id, causando
type/valor inesperado; substitua-o por @Param('jobId') para extrair a string do
parâmetro de rota (ou use o nome exato do parâmetro de rota se for diferente),
mantendo o resto da assinatura igual.
| await this.jobsRepository | ||
| .update(jobId, { | ||
| description: completeData.description, | ||
| prerequisites: completeData.requirements.join(', '), | ||
| benefits: completeData.benefits?.join(', '), | ||
| typeContract: completeData.contractType, | ||
| contractType: completeData.journey, | ||
| contractText: completeData.selectionProcess.join(' -> '), | ||
| content: completeData.additionalInfo, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "*job*.entity.ts" -o -name "*job*.model.ts" | head -20Repository: SouJunior/vagas-api
Length of output: 101
🏁 Script executed:
rg "class JobsEntity|interface.*Job" --type ts -A 30Repository: SouJunior/vagas-api
Length of output: 2249
🏁 Script executed:
rg "contractType|typeContract" --type ts -B 2 -A 2Repository: SouJunior/vagas-api
Length of output: 6238
🏁 Script executed:
rg "duration|journey" src/modules/jobs/dtos/complete-job.dto.ts -B 2 -A 2Repository: SouJunior/vagas-api
Length of output: 137
🏁 Script executed:
cat -n src/database/entities/jobs.entity.ts | sed -n '1,100p'Repository: SouJunior/vagas-api
Length of output: 3015
🏁 Script executed:
cat -n src/modules/jobs/dtos/complete-job.dto.tsRepository: SouJunior/vagas-api
Length of output: 2471
Corrigir atribuição de campos em publishJob (linhas 48-49).
A atribuição está incorreta:
contractTyperecebecompleteData.journey(jornada de trabalho) quando deveria receber dados de duração/tipo de contratotypeContractrecebe corretamentecompleteData.contractType(enum CLT/PJ/OTHER)
O campo contractType está sendo preenchido com dados de jornada de trabalho (ex: "40 horas semanais"), causando perda de informação. Verifique qual dado deveria ser atribuído a contractType ou se este campo deve ser removido da atualização.
🤖 Prompt for AI Agents
In src/modules/jobs/repository/job.repository.ts around lines 43 to 51, the
update assigns contract fields incorrectly: currently typeContract is set to
completeData.contractType (correct) but contractType is incorrectly set to
completeData.journey (work schedule). Change the update so that typeContract
remains completeData.contractType and contractType receives the appropriate
contract duration/type field (e.g., completeData.contractDuration or the correct
property name from CompleteData); if there is no separate duration field, remove
contractType from the update to avoid overwriting it with journey — verify the
actual CompleteData property and use that instead.
| if (!job) { | ||
| throw new BadRequestException('Job not found'); | ||
| } |
There was a problem hiding this comment.
Inconsistência no tipo de exceção para "Job not found".
O JobOwnerGuard usa NotFoundException para vaga não encontrada, mas este serviço usa BadRequestException. Se o guard for aplicado antes do serviço, esta verificação será redundante. Caso contrário, considere usar NotFoundException para consistência semântica.
if (!job) {
- throw new BadRequestException('Job not found');
+ throw new NotFoundException('Job not found');
}Não esqueça de adicionar o import:
-import { BadRequestException, Injectable } from '@nestjs/common';
+import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';📝 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.
| if (!job) { | |
| throw new BadRequestException('Job not found'); | |
| } | |
| import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; | |
| if (!job) { | |
| throw new NotFoundException('Job not found'); | |
| } |
🤖 Prompt for AI Agents
In src/modules/jobs/services/publish-job.service.ts around lines 13 to 15,
replace the thrown BadRequestException('Job not found') with
NotFoundException('Job not found') to match the semantic used by JobOwnerGuard
and maintain consistency; also add the corresponding import for
NotFoundException from @nestjs/common at the top of the file (and remove the
unused BadRequestException import if no longer used).
| export function CancelJobSwagger() { | ||
| return applyDecorators( | ||
| ApiResponse({ | ||
| status: HttpStatus.OK, | ||
| description: 'Vaga cancelada com sucesso', | ||
| }), | ||
| ApiResponse({ | ||
| status: HttpStatus.BAD_REQUEST, | ||
| description: 'Modelo de erro', | ||
| type: BadRequestSwagger, | ||
| }), | ||
| ApiResponse({ | ||
| status: HttpStatus.UNAUTHORIZED, | ||
| description: 'Não autorizado', | ||
| type: UnauthorizedSwagger, | ||
| }), | ||
| ApiResponse({ | ||
| status: HttpStatus.FORBIDDEN, | ||
| description: 'Sem permissão para modificar esta vaga', | ||
| }), | ||
| ApiOperation({ | ||
| summary: 'Cancelar vaga publicada', | ||
| }), | ||
| ); |
There was a problem hiding this comment.
Falta documentar a resposta 404 (Not Found).
O JobOwnerGuard lança NotFoundException quando a vaga não é encontrada. A documentação Swagger deve incluir essa resposta para refletir corretamente o comportamento da API.
ApiResponse({
status: HttpStatus.FORBIDDEN,
description: 'Sem permissão para modificar esta vaga',
}),
+ ApiResponse({
+ status: HttpStatus.NOT_FOUND,
+ description: 'Vaga não encontrada',
+ }),
ApiOperation({
summary: 'Cancelar vaga publicada',
}),📝 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.
| export function CancelJobSwagger() { | |
| return applyDecorators( | |
| ApiResponse({ | |
| status: HttpStatus.OK, | |
| description: 'Vaga cancelada com sucesso', | |
| }), | |
| ApiResponse({ | |
| status: HttpStatus.BAD_REQUEST, | |
| description: 'Modelo de erro', | |
| type: BadRequestSwagger, | |
| }), | |
| ApiResponse({ | |
| status: HttpStatus.UNAUTHORIZED, | |
| description: 'Não autorizado', | |
| type: UnauthorizedSwagger, | |
| }), | |
| ApiResponse({ | |
| status: HttpStatus.FORBIDDEN, | |
| description: 'Sem permissão para modificar esta vaga', | |
| }), | |
| ApiOperation({ | |
| summary: 'Cancelar vaga publicada', | |
| }), | |
| ); | |
| export function CancelJobSwagger() { | |
| return applyDecorators( | |
| ApiResponse({ | |
| status: HttpStatus.OK, | |
| description: 'Vaga cancelada com sucesso', | |
| }), | |
| ApiResponse({ | |
| status: HttpStatus.BAD_REQUEST, | |
| description: 'Modelo de erro', | |
| type: BadRequestSwagger, | |
| }), | |
| ApiResponse({ | |
| status: HttpStatus.UNAUTHORIZED, | |
| description: 'Não autorizado', | |
| type: UnauthorizedSwagger, | |
| }), | |
| ApiResponse({ | |
| status: HttpStatus.FORBIDDEN, | |
| description: 'Sem permissão para modificar esta vaga', | |
| }), | |
| ApiResponse({ | |
| status: HttpStatus.NOT_FOUND, | |
| description: 'Vaga não encontrada', | |
| }), | |
| ApiOperation({ | |
| summary: 'Cancelar vaga publicada', | |
| }), | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/shared/Swagger/decorators/jobs/cancel-job.swagger.ts around lines 6 to
29, the Swagger responses miss the 404 Not Found case thrown by JobOwnerGuard;
add an ApiResponse for status HttpStatus.NOT_FOUND with a descriptive message
(e.g., 'Vaga não encontrada') and the appropriate error DTO/type (e.g.,
NotFoundSwagger), and if that type doesn't exist, import or create it and use it
in the ApiResponse so Swagger reflects the NotFoundException behavior.
MikaelMelo1
left a comment
There was a problem hiding this comment.
Alguns pontos precisam de ajustes.
There was a problem hiding this comment.
-
Dá uma olhada na parte de enum no @column. os campos estão como string, o ideal é tipar direto com o enum e declarar no decorator.
-
Veja a questão de typeContract e contractType. Os dois estão representando a mesma coisa, então seria bom padronizar em um só campo
-
Revisar o campo openEndedContract, que está com default e nullable juntos.
-
Campos de texto (description, prerequisites, contractText) podem ter o tipo/tamanho mais definido
There was a problem hiding this comment.
O Enum no contractType: dá uma olhada no alinhamento do DTO (usa enum) e a entity, pra garantir que os valores salvos sejam compatíveis.
typeContract e contractType: bom seria padronizar um
Campos array no DTO (requirements, benefits, selectionProcess): o banco ta preparado pra receber o array?
Tamanho dos textos (description, additionalInfo): o limite do DTO bate com o banco?
There was a problem hiding this comment.
city + modality: a validação está ok, bom é confirmar se no fluxo da aplicação o campo city é realmente tratado como obrigatório quando não for remoto.
salaryMin e salaryMax: seria bom garantir no serviço que o salário mínimo não seja maior que o máximo, pra evitar inconsistência de dados.
@IsNumber: só conferir se o ValidationPipe está com transform: true, senão esses campos podem chegar como string.
company_id: o campo não tem validação nem ApiProperty. avaliar se isso é intencional ou se precisa de um mínimo de padronização.
There was a problem hiding this comment.
jobId vindo dos params: vale confirmar se o id sempre existe na rota. Se não, vai resultar no repo undefined.
Comparação job.company_id !== user.id: conferir se os dois valores têm o mesmo tipo (ex: ambos string ou number) assim vamos evitar falha na comparação
Mensagens de erro: estão claras , só avaliar se esse nível de detalhe é o esperado para um ForbiddenException (nada de urgente)
Responsabilidade do Guard: o guard está buscando no banco, o que funciona, mas vale só validar se isso está alinhado com o restante do projeto
There was a problem hiding this comment.
publishJob: da uma atenção notypeContract e contractType
Conversão de arrays para string (join): ta certo o formato esperado?
orderByColumn vindo do request: está sendo validado?
searchJobs: o ILIKE esta sendo montado direto na string.
findOneById: esta retornando como any, consegue tipar?
There was a problem hiding this comment.
as DELETE e PATCH ':id estão com responsabilidade parecidas, da uma olhada.
Guards: JobOwnerGuard está aplicado nas rotas, verifique isso pra mim
archivedJob: @param() está sem chave (jobId: string), o valor ta chegando?
Search (/search/:keyword): mistura param, query e body., está certo?
Swagger imports duplicados/nome estranho (get-all-jobs-of-logged-company.swagger copy): da uma organizada
There was a problem hiding this comment.
O bom seria não colocar no PR junto.
There was a problem hiding this comment.
Da uma conferida se é isso mesmo.
There was a problem hiding this comment.
Da uma conferida no que o Bot falou, o resto está ok.
There was a problem hiding this comment.
FORBIDDEN: está alinhado com o comportamento do guard?
BadRequestSwagger cobre todos os cenários de erro?


Implementação completa do sistema de gerenciamento de ciclo de vida de vagas, permitindo que empresas criem rascunhos, publiquem, cancelem e excluam vagas de acordo com regras de negócio específicas.
🎯 Objetivo
Permitir que empresas criem vagas em etapas, salvando primeiro um rascunho com informações básicas e posteriormente completando os dados para publicação, melhorando a experiência do usuário e reduzindo erros no cadastro de vagas.
Summary by CodeRabbit
Notas de Lançamento
✏️ Tip: You can customize this high-level summary in your review settings.