Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/external application2 #186

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
fd325b7
:construction: Feat (jobs,users,candidacy): Criação da entidade candi…
leticiazalasik Sep 28, 2024
9cb2da0
:sparkles: Feat (candidacy-entity, candidacy-repository, candidacy-st…
leticiazalasik Oct 7, 2024
4ec73c0
:bug: Fix (jobs-entity): Correções da ligação de tabelas.
leticiazalasik Oct 7, 2024
ebdf7d7
:bug: Fix (candidacy-entity): Padronização do nome do atributo para job.
leticiazalasik Oct 7, 2024
b3ad650
:sparkles: Feat (candidacy-entity, candidacy-repository) optado pelo …
leticiazalasik Oct 7, 2024
f2d2d2f
:sparkles: Feat Atualização do repositório local
leticiazalasik Oct 8, 2024
ae482a4
:sparkles: Feat Atualização do repositório local
leticiazalasik Oct 8, 2024
363a543
:sparkles: Feat Atualização do repositório local
leticiazalasik Oct 9, 2024
2bd0727
Update README.md
leticiazalasik Nov 1, 2024
79fa4d3
:ok_hand: feat: Acrescentando o campo status na entidade application.
leticiazalasik Nov 9, 2024
e5857d4
:sparkles: feat: feat: Criação da service que busca o histórico de v…
leticiazalasik Nov 9, 2024
dbea464
:sparkles: feat: feat: Criação da controller que tem o endpoint get.
leticiazalasik Nov 9, 2024
e382ed6
feat(endpoint): Criar endpoint para atualizar candidatura
joselazarojunior Nov 23, 2024
56d95c1
:construction: Fix: (applications.entity) Indíces na chave estrangei…
leticiazalasik Dec 4, 2024
4461b13
:construction: Feat: (applications.entity, application-status.enum) …
leticiazalasik Dec 4, 2024
9bfc59b
:construction: Feat: (applicationHistory.service, application-histor…
leticiazalasik Dec 10, 2024
90507ab
fix(endpoint): Criar endpoint para atualizar candidatura
joselazarojunior Dec 13, 2024
9d48113
fix(endpoint): Criar endpoint para atualizar candidatura
joselazarojunior Dec 13, 2024
32efa36
:construction: Fix: (applicationHistory.controller, application-hist…
leticiazalasik Dec 17, 2024
c9009f8
Merge branch 'feature/external-application2' of https://github.com/le…
leticiazalasik Dec 17, 2024
00eee0b
:construction: Fix: mudança na resposta de getAppStatus, cpnsulta ma…
leticiazalasik Dec 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 0 additions & 113 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,115 +10,6 @@

---

## Menu

### [Abrir e rodar o projeto](#abrir_e_rodar_o_projeto)

### [Acesso ao projeto](#acesso_ao_projeto)

### [Rodando Localmente](#rodando_localmente)

### [Rodando com Docker](#instalando_o_docker)

### [Stack Utilizada](#stack_utilizada)

### [Equipe Back-end](#equipe_do_backend)

### Pre requisitos:
* git
* docker
* insomnia

---

<a id="abrir_e_rodar_o_projeto"></a>

## 🛠️ Abrir e rodar o projeto


Clone o projeto na janela que abriu com o seguinte comando:

```bash
git clone https://github.com/SouJunior/linkedin-backend.git
```


---

`docker-compose up -d`


O projeto vai estar rodando em:

`localhost:3000`

agora basta seguir para [Instalando o Insomnia](#instalando_o_insomnia)

---

<a id="acesso_ao_projeto"></a>

## 📁 Acesso ao projeto

Entre na pasta do projeto pelo Visual Studio, Abra o terminal do visual Studio e
vá para o diretório do projeto com o comando:

```bash
cd vagas-api

Instale as dependências

```bash
npm i
```

Feito a instalação dos pacotes basta renomear o arquivo `.env.example` para `.env` e preencher com as suas informações do banco de dados, no caso estamos usando o PostgreSQL

Arquivo .env

```bash
PORT=3000 #Porta que seu projeto vai rodar na sua maquina

# JWT
SECRET_KEY= qualquerStringAqui #Uma string qualquer, chave para gerar o JWT

# TYPEORM_CONNECTION
TYPEORM_CONNECTION=postgres
TYPEORM_HOST= #Host name do seu banco (geralmente quando esta na sua maquina fica localhost)
TYPEORM_PORT=5432 #A porta geralmente é 5432, se no seu caso for outra porta basta alterar
TYPEORM_USERNAME= # Seu usuario do banco postgress
TYPEORM_PASSWORD= # Sua senha do banco postgress
TYPEORM_DATABASE= # Sua database do banco de dados.
```

---

<a id="rodando_localmente"></a>

## 🌐 Rodando localmente

Inicie o servidor

```bash
npm run start:dev
```

---


Após a instalação do Wsl, vamos até a pasta do projeto e rodar o comando

`docker-compose up -d`


O projeto vai estar rodando em:

`localhost:3000`

---

<a id="stack_utilizada"></a>

## Stack utilizada ⚙

**Linguagens:**
Expand Down Expand Up @@ -170,10 +61,6 @@ O projeto vai estar rodando em:

Se você tiver algum feedback, por favor nos deixe saber por meio do nosso fazendo uma [contribuição](#contribuição).

## Contribuição

Contribuições são sempre bem-vindas!

## Usado por

Esse projeto é usado pela [SouJunior](https://github.com/SouJunior).
Expand Down
6 changes: 4 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"axios": "^1.7.7",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"class-validator": "^0.14.1",
"cpf-cnpj-validator": "^1.0.3",
"dotenv": "^16.4.5",
"handlebars": "^4.7.8",
Expand Down
89 changes: 78 additions & 11 deletions src/app.controller.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verifique a separação das rotas.

Verifique no status, se precisa de enum.

Utilize decorators pra validar os parametros da rota.

Crie exceções personalizadas (tratamento de erros)

Verifique se o @ApiOperation não está sendo utilizada, utilize outra pra documentar melhor as rotas no swagger.

Padronize a respota pra seguir o mesmo formato das outras rotas, retornando assim o objeto como status e data.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verifique a separação das rotas.
Feito!

Verifique no status, se precisa de enum.

  • Criado status.enum.ts :
    IN_PROGRESS = 'em andamento',
    CLOSED = 'encerrada',
    NOT_INTERESTED = 'sem interesse',

Utilize decorators pra validar os parametros da rota.
Criado update-status.dto.ts
ءء
Crie exceções personalizadas (tratamento de erros)
Criados os arquivos de custom exception:
bad-request.exception.ts
not-found.exception.ts

Verifique se o @ApiOperation não está sendo utilizada, utilize outra pra documentar melhor as rotas no swagger.
Atualizado para utilizar os decorators:

  • @ApiResponse,
  • @ApiParam,
    e @ApiBody

Padronize a resposta pra seguir o mesmo formato das outras rotas, retornando assim o objeto como status e data.
Padronizado. Retornando um objeto com status e data.

Original file line number Diff line number Diff line change
@@ -1,31 +1,98 @@
import { Controller, Get, Req, Res } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import {
Controller,
Get,
Req,
Res,
Patch,
Param,
Body,
HttpCode,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { ApiTags, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { AppService } from './app.service';
import { SwaggerHealthCheck } from './shared/Swagger/decorators/app/health-check.swagger.decorator';

import { Request, Response } from 'express';
import { UpdateStatusDto } from './modules/applications/dtos/update-status.dto';
import { CustomBadRequestException } from './modules/applications/exceptions/bad-request.exception';
import { CustomNotFoundException } from './modules/applications/exceptions/not-found.exception';

@ApiTags('Status')
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}

@Get()
@ApiOperation({
summary: 'Show status of operation',
@ApiResponse({
status: 200,
description: 'Status of operation returned successfully.',
})
getAppStatus(@Req() req: Request) {
const baseUrl = req.protocol + '://' + req.get('host');
return this.appService.getAppStatus(baseUrl);
const status = this.appService.getAppStatus(baseUrl);
return { status: 'success', data: status };
}

@Get('/health-check')
@SwaggerHealthCheck()
@ApiOperation({
summary: 'Retorna status dos serviços de email e banco de dados',
@ApiResponse({
status: 200,
description:
'Health check status of email and database services returned successfully.',
})
async getHealthCheck(@Res() res: Response) {
const { status, data } = await this.appService.getHealthCheck();
return res.status(status).send({ status: 'success', data });
}

@Patch(':userId/:applicationId')
@HttpCode(200)
@UsePipes(new ValidationPipe({ transform: true }))
@ApiParam({ name: 'userId', required: true, description: 'ID do usuário' })
@ApiParam({
name: 'applicationId',
required: true,
description: 'ID da aplicação',
})
@ApiBody({ type: UpdateStatusDto })
@ApiResponse({
status: 200,
description: 'Status atualizado com sucesso.',
})
@ApiResponse({
status: 400,
description: 'Novo status é obrigatório.',
})
async getHealthCheck(@Res() res: Response){
const {status, data} = await this.appService.getHealthCheck();
return res.status(status).send(data);
@ApiResponse({
status: 404,
description: 'Candidatura não encontrada ou não pertence ao usuário.',
})
async updateStatus(
@Param() params: UpdateStatusDto,
@Body() body: UpdateStatusDto,
) {
const { userId, applicationId, status } = { ...params, ...body };

if (!status) {
throw new CustomBadRequestException('Novo status é obrigatório');
}
Comment on lines +77 to +79
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Melhore o tratamento de erro para status inválido

A validação do status poderia ser feita no DTO usando o decorator @IsEnum() do class-validator.

export class UpdateStatusDto {
+  @IsEnum(ApplicationStatus, { message: 'Status inválido' })
   status: ApplicationStatus;
}

Committable suggestion skipped: line range outside the PR's diff.


const application = await this.appService.updateStatus(
userId,
applicationId,
status,
);

if (!application) {
throw new CustomNotFoundException(
'Candidatura não encontrada ou não pertence ao usuário',
);
}

return {
status: 'success',
data: { message: 'Status atualizado com sucesso', application },
};
}
}
84 changes: 56 additions & 28 deletions src/app.service.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verifique se a resposta do getAppStatus é suficiente.

getHealthCheck: Consulta eficiente seria verificar a conexão com o BD ou buscar um unico registro. Pq a consulta atual busca todos mas apenas verifica se o resultado é null ou indefinid.

databaseStatus e mailerStatus: crie enums pra represetar os status "OK" e "DOWN", assim torna o código mais claro

updateStatus: o user_id está sendo usado mas não está sendo validado.

status: definir um enum

Lançar exceções especificas quando não for encontrada candidatura ou até mesmo status for inválido.

Verifique a possibilidade de criar serviços separados pra cada área, assim melhora a organização.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Olá, tentei fazer isso tudo, mas precisa de correção, principalmente na divisão de serviços e não fiz enums pra represetar os status "OK" e "DOWN", fiz diretamente no app pois achei que já tinha muit coisa separada.

Original file line number Diff line number Diff line change
@@ -1,70 +1,98 @@
import { Injectable } from '@nestjs/common';
import { MailService } from './modules/mails/mail.service';
import { UserRepository } from './modules/user/repository/user.repository';
import { PageOptionsDto } from './shared/pagination';
import { Order } from './shared/pagination';
import { PageOptionsDto, Order } from './shared/pagination';
import { InjectRepository } from '@nestjs/typeorm';
import {
ApplicationEntity,
ApplicationStatus,
} from './database/entities/applications.entity';
import { Repository } from 'typeorm';
import { CustomBadRequestException } from './modules/applications/exceptions/bad-request.exception';

@Injectable()
export class AppService {
constructor(
private mailService: MailService,
private userRepository: UserRepository
){}
private userRepository: UserRepository,
@InjectRepository(ApplicationEntity)
private applicationRepository: Repository<ApplicationEntity>,
) {}

getAppStatus(baseUrl: string) {
return `<div style=text-align:center><a target="_blank" href="https://www.linkedin.com/company/soujunior/"><svg font-family="Times New Roman" font-size="16" height="299.96" viewBox="0 0 854 300" width="854.56" xmlns="http://www.w3.org/2000/svg" style="width:854.56px; height:299.96px; font-family:'Times New Roman'; font-size:16px; position:relative; z-index:1" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.text {font-size: 90px;font-weight: 700;font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;}.desc {font-size: 20px;font-weight: 500;font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;}.text, .desc {animation: fadeIn 1.2s ease-in-out forwards;}@keyframes fadeIn { from {opacity: 0; } to {opacity: 1; }};</style><g transform="translate(427, 150) scale(1, 1) translate(-427, -150)"><path d="" fill="#2088f2" opacity="0.4"><animate attributeName="d" begin="0s" calcmod="spline" dur="20s" keySplines="0.2 0 0.2 1;0.2 0 0.2 1;0.2 0 0.2 1" keyTimes="0;0.333;0.667;1" repeatCount="indefinite" values="M0 0L 0 220Q 213.5 260 427 230T 854 255L 854 0 Z;M0 0L 0 245Q 213.5 260 427 240T 854 230L 854 0 Z;M0 0L 0 265Q 213.5 235 427 265T 854 230L 854 0 Z;M0 0L 0 220Q 213.5 260 427 230T 854 255L 854 0 Z" /></path><path d="" fill="#2088f2" opacity="0.4"><animate attributeName="d" begin="-10s" calcmod="spline" dur="20s" keySplines="0.2 0 0.2 1;0.2 0 0.2 1;0.2 0 0.2 1" keyTimes="0;0.333;0.667;1" repeatCount="indefinite" values="M0 0L 0 235Q 213.5 280 427 250T 854 260L 854 0 Z;M0 0L 0 250Q 213.5 220 427 220T 854 240L 854 0 Z;M0 0L 0 245Q 213.5 225 427 250T 854 265L 854 0 Z;M0 0L 0 235Q 213.5 280 427 250T 854 260L 854 0 Z" /></path></g><text alignment-baseline="middle" class="text" stroke="#none" stroke-width="1" text-anchor="middle" x="50%" y="38%" style="font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; font-size:90px; font-weight:700; alignment-baseline:middle; fill:#ffffff; stroke-width:1; text-anchor:middle">Sou Junior</text><text alignment-baseline="middle" class="desc" text-anchor="middle" x="52%" y="61%" style="font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; font-size:20px; font-weight:500; alignment-baseline:middle; fill:#ffffff; text-anchor:middle">Projeto Opensource para melhorar o match entre os profissionais Juniors e Empresas!</text></svg></a></div>`;
}

async getHealthCheck(){
async getHealthCheck() {
const databaseStatus = await this.checkDatabase();
const mailerStatus = await this.checkEmail();
const data = {
databaseStatus,
mailerStatus
}
mailerStatus,
};
return {
status: 201,
data
data,
};
}


private async checkDatabase(){
try{
private async checkDatabase() {
try {
const options: PageOptionsDto = {
page: 1,
take: 10,
orderByColumn: 'id',
order: Order.ASC
order: Order.ASC,
};
const allUsers = await this.userRepository.getAllUsers(options);
if (allUsers == null || allUsers == undefined){
return "DOWN";
if (allUsers == null || allUsers == undefined) {
return 'DOWN';
}
return "OK";
}
catch(error){
return "DOWN";
return 'OK';
} catch (error) {
return 'DOWN';
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Otimize a verificação do banco de dados

O método atual busca todos os usuários para verificar a saúde do banco de dados, o que pode ser ineficiente. Considere usar uma consulta mais leve.

  private async checkDatabase() {
    try {
-     const options: PageOptionsDto = {
-       page: 1,
-       take: 10,
-       orderByColumn: 'id',
-       order: Order.ASC,
-     };
-     const allUsers = await this.userRepository.getAllUsers(options);
+     const result = await this.userRepository
+       .createQueryBuilder()
+       .select('1')
+       .limit(1)
+       .getRawOne();
-     if (allUsers == null || allUsers == undefined) {
+     if (!result) {
        return 'DOWN';
      }
      return 'OK';
    } catch (error) {
      return 'DOWN';
    }
  }
📝 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.

Suggested change
private async checkDatabase() {
try {
const options: PageOptionsDto = {
page: 1,
take: 10,
orderByColumn: 'id',
order: Order.ASC
order: Order.ASC,
};
const allUsers = await this.userRepository.getAllUsers(options);
if (allUsers == null || allUsers == undefined){
return "DOWN";
if (allUsers == null || allUsers == undefined) {
return 'DOWN';
}
return "OK";
}
catch(error){
return "DOWN";
return 'OK';
} catch (error) {
return 'DOWN';
}
private async checkDatabase() {
try {
const result = await this.userRepository
.createQueryBuilder()
.select('1')
.limit(1)
.getRawOne();
if (!result) {
return 'DOWN';
}
return 'OK';
} catch (error) {
return 'DOWN';
}
}

}
private async checkEmail(){
try{

private async checkEmail() {
try {
await this.mailService.sendMail({
subject: 'HealthCheck',
template: './health',
context: {
arg1: "Argumento1",
arg2: "Argumento2"
arg1: 'Argumento1',
arg2: 'Argumento2',
},
email: '[email protected]'
email: '[email protected]',
});
return "OK";
return 'OK';
} catch (error) {
return 'DOWN';
}
catch(error){
return "DOWN";
}

async updateStatus(
user_id: string,
applicationId: string,
status: string,
): Promise<ApplicationEntity | null> {
const application = await this.applicationRepository.findOne({
where: { id: applicationId, user_id },
});

if (!application) {
return null;
}

const validStatus = Object.values(ApplicationStatus).includes(
status as ApplicationStatus,
);
if (!validStatus) {
throw new CustomBadRequestException('Status inválido');
}

application.status = status as ApplicationStatus;
await this.applicationRepository.save(application);
return application;
}

}
Loading