Skip to content

Commit

Permalink
Merge pull request #185 from SouJunior/feat-473
Browse files Browse the repository at this point in the history
feat(candidacy): candidacy entity, service & controller
  • Loading branch information
MikaelMelo1 authored Dec 5, 2024
2 parents c651cce + 3697025 commit bda62db
Show file tree
Hide file tree
Showing 16 changed files with 459 additions and 6 deletions.
8 changes: 3 additions & 5 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { PassportModule } from '@nestjs/passport';
import { UserRepository } from './modules/user/repository/user.repository';
import { UsersEntity } from './database/entities/users.entity';
import { AlertsModule } from './modules/alert/alerts.module';

import { CandidacyModule } from './modules/candidacy/candidacy.module';

@Module({
imports: [
Expand All @@ -44,11 +44,9 @@ import { AlertsModule } from './modules/alert/alerts.module';
ApplicationsModule,
TypeOrmModule.forFeature([UsersEntity]),
AlertsModule,
CandidacyModule,
],
controllers: [AppController],
providers: [
AppService,
UserRepository
],
providers: [AppService, UserRepository],
})
export class AppModule {}
43 changes: 43 additions & 0 deletions src/database/entities/candidacy.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
Column,
Entity,
PrimaryGeneratedColumn,
ManyToOne,
JoinColumn,
} from 'typeorm';
import { UsersEntity } from './users.entity';
import { JobsEntity } from './jobs.entity';
import { CandidacyStatus } from './candidancy-status.enum';

@Entity('tb_candidacies')
export class CandidacyEntity {
@PrimaryGeneratedColumn('uuid')
id: string;

@Column('uuid', { name: 'job_id' })
jobId: string;

@Column('uuid', { name: 'user_id' })
userId: string;

@Column({ type: 'enum', enum: CandidacyStatus })
status: CandidacyStatus;

@Column({
type: 'date',
name: 'date_candidacy',
default: () => 'CURRENT_TIMESTAMP',
})
dateCandidacy: Date;

@Column({ name: 'date_closing', type: 'timestamp', nullable: true })
dateClosing: Date;

@ManyToOne(() => UsersEntity)
@JoinColumn({ name: 'user_id' })
user: UsersEntity;

@ManyToOne(() => JobsEntity)
@JoinColumn({ name: 'job_id' })
job: JobsEntity;
}
5 changes: 5 additions & 0 deletions src/database/entities/candidancy-status.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum CandidacyStatus {
InProgress = 'em andamento',
Closed = 'encerrada',
NoInterest = 'sem interesse',
}
4 changes: 4 additions & 0 deletions src/database/entities/users.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { ApplicationEntity } from './applications.entity';
import { CurriculumEntity } from './curriculum.entity';
import { PersonalDataEntity } from './personal-data.entity';
import { CandidacyEntity } from './candidacy.entity';

enum RolesEnum {
ADMIN = 'ADMIN',
Expand Down Expand Up @@ -72,6 +73,9 @@ export class UsersEntity {
@OneToMany(() => ApplicationEntity, (application) => application.user)
applications: ApplicationEntity[];

@OneToMany(() => CandidacyEntity, (candidacy) => candidacy.user)
candidacies: CandidacyEntity[];

@CreateDateColumn()
created_at: Date;

Expand Down
78 changes: 78 additions & 0 deletions src/database/migrations/1731094752487-Candidacy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
MigrationInterface,
QueryRunner,
Table,
TableForeignKey,
} from 'typeorm';

export class Candidacy1731094752487 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: 'tb_candidacies',
columns: [
{
name: 'id',
type: 'uuid',
isPrimary: true,
generationStrategy: 'uuid',
default: 'uuid_generate_v4()',
},
{
name: 'job_id',
type: 'uuid',
},
{
name: 'user_id',
type: 'uuid',
},
{
name: 'status',
type: 'enum',
enum: ['em andamento', 'encerrada', 'sem interesse'],
},
{
name: 'date_candidacy',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
},
{
name: 'date_closing',
type: 'timestamp',
isNullable: true,
},
],
}),
true,
);

await queryRunner.createForeignKey(
'tb_candidacies',
new TableForeignKey({
columnNames: ['user_id'],
referencedTableName: 'tb_users',
referencedColumnNames: ['id'],
}),
);

await queryRunner.createForeignKey(
'tb_candidacies',
new TableForeignKey({
columnNames: ['job_id'],
referencedTableName: 'tb_jobs',
referencedColumnNames: ['id'],
}),
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
const table = await queryRunner.getTable('tb_candidacies');
const foreignKeys = table.foreignKeys.filter(
(fk) =>
fk.columnNames.indexOf('user_id') !== -1 ||
fk.columnNames.indexOf('job_id') !== -1,
);
await queryRunner.dropForeignKeys('tb_candidacies', foreignKeys);
await queryRunner.dropTable('tb_candidacies');
}
}
2 changes: 1 addition & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ async function bootstrap() {
console.info(`🚀🚀 App listening on port ${process.env.PORT || 3000} 🚀🚀`);
});
}
bootstrap();
bootstrap();
14 changes: 14 additions & 0 deletions src/modules/candidacy/candidacy.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { TypeOrmModule } from '@nestjs/typeorm';
import { Module } from '@nestjs/common';
import { CandidacyEntity } from 'src/database/entities/candidacy.entity';
import { CandidacyRepository } from './repository/candidacy.repository';
import { CandidacyService } from './service/candidacy.service';
import { CandidacyController } from './controller/candidacy.controller';

@Module({
imports: [TypeOrmModule.forFeature([CandidacyEntity])],
controllers: [CandidacyController],
providers: [CandidacyService, CandidacyRepository],
exports: [CandidacyService],
})
export class CandidacyModule {}
53 changes: 53 additions & 0 deletions src/modules/candidacy/controller/candidacy.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
BadRequestException,
Body,
Controller,
Get,
Patch,
Post,
UseGuards,
} from '@nestjs/common';
import { CandidacyService } from '../service/candidacy.service';
import { CreateCandidacyDto } from '../dto/create-candidacy.dto';
import { LoggedUser } from 'src/modules/auth/decorator/logged-user.decorator';
import { UsersEntity } from 'src/database/entities/users.entity';
import { AuthGuard } from '@nestjs/passport';
import { UpdateCandidacyDto } from '../dto/update-candidacy.dto';
import { CandidacyStatus } from 'src/database/entities/candidancy-status.enum';
import { ApiTags } from '@nestjs/swagger';
import { CreateCandidacySwagger } from 'src/shared/Swagger/decorators/candidacy/create-candidacy.swagger';
import { GetCandidaciesSwagger } from 'src/shared/Swagger/decorators/candidacy/get-candidacies.swagger';
import { UpdateCandidacySwagger } from 'src/shared/Swagger/decorators/candidacy/update-candidacy.swagger';

@ApiTags('Candidacy')
@Controller('candidacy')
@UseGuards(AuthGuard('jwt'))
export class CandidacyController {
constructor(private readonly candidacyService: CandidacyService) {}

@Post()
@CreateCandidacySwagger()
async createCandidacy(@Body() createCandidacyDTO: CreateCandidacyDto) {
return await this.candidacyService.create(createCandidacyDTO);
}

@Get()
@GetCandidaciesSwagger()
async getCandidacies(@LoggedUser() user: UsersEntity) {
return await this.candidacyService.getCandidacyByUserId(user.id);
}

@Patch()
@UpdateCandidacySwagger()
async updateCandidacy(@Body() updateCandidacyDto: UpdateCandidacyDto) {
if (updateCandidacyDto.status === CandidacyStatus.InProgress) {
throw new BadRequestException(
'Não é possível atualizar para o status "em andamento"',
);
}
return await this.candidacyService.closeCandidacy(
updateCandidacyDto.id,
updateCandidacyDto.status,
);
}
}
14 changes: 14 additions & 0 deletions src/modules/candidacy/dto/create-candidacy.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsUUID, IsNotEmpty } from 'class-validator';

export class CreateCandidacyDto {
@IsUUID()
@IsNotEmpty()
@ApiProperty({ type: 'string', format: 'uuid' })
userId: string;

@IsUUID()
@IsNotEmpty()
@ApiProperty({ type: 'string', format: 'uuid' })
jobId: string;
}
15 changes: 15 additions & 0 deletions src/modules/candidacy/dto/update-candidacy.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty, IsUUID } from 'class-validator';
import { CandidacyStatus } from 'src/database/entities/candidancy-status.enum';

export class UpdateCandidacyDto {
@ApiProperty({ type: 'string', format: 'uuid' })
@IsUUID()
@IsNotEmpty()
id: string;

@ApiProperty({ enum: CandidacyStatus })
@IsNotEmpty()
@IsEnum(CandidacyStatus)
status: CandidacyStatus;
}
69 changes: 69 additions & 0 deletions src/modules/candidacy/repository/candidacy.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {
BadRequestException,
Injectable,
InternalServerErrorException,
NotFoundException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { CandidacyEntity } from '../../../database/entities/candidacy.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { CandidacyStatus } from 'src/database/entities/candidancy-status.enum';

@Injectable()
export class CandidacyRepository {
constructor(
@InjectRepository(CandidacyEntity)
private candidacyRepository: Repository<CandidacyEntity>,
) {}

async createCandidacy(candidacy: CandidacyEntity): Promise<CandidacyEntity> {
return this.candidacyRepository.save(candidacy);
}

async findAllByUserId(userId: string): Promise<CandidacyEntity[]> {
if (!userId) {
throw new BadRequestException('userId é obrigatório');
}
try {
const candidacy = await this.candidacyRepository.find({
where: { userId: userId },
});
if (!candidacy.length) {
throw new NotFoundException(
'Nenhuma candidatura encontrada para este usuário',
);
}
return candidacy;
} catch (error) {
throw new BadRequestException(
'Erro ao buscar candidaturas: ' + error.message,
);
}
}

async updateStatus(
id: string,
status: CandidacyStatus,
): Promise<CandidacyEntity | null> {
try {
const candidacy = await this.candidacyRepository.findOne({
where: { id },
});
if (!candidacy) {
throw new NotFoundException('Candidatura não encontrada');
}
candidacy.status = status;
await this.candidacyRepository.save(candidacy);

return candidacy;
} catch (error) {
if (error instanceof NotFoundException) {
throw error;
} else {
throw new InternalServerErrorException(
'Erro ao atualizar o status da candidatura: ' + error.message,
);
}
}
}
}
18 changes: 18 additions & 0 deletions src/modules/candidacy/service/candidacy.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { CandidacyService } from '../candidacy.service';

describe('CandidacyService', () => {
let service: CandidacyService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [CandidacyService],
}).compile();

service = module.get<CandidacyService>(CandidacyService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
Loading

0 comments on commit bda62db

Please sign in to comment.