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

Fullstack Challenge - Romulo Evangelista #3

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0c83cb9
init challenge
romulo-evangelista Mar 5, 2021
561f3bc
connect DB and CRUD
romulo-evangelista Mar 6, 2021
544cb44
fix bugs in remove routes
romulo-evangelista Mar 6, 2021
a4394ac
add frontend
romulo-evangelista Mar 6, 2021
0a8a54a
using nestjs in backend
romulo-evangelista Mar 6, 2021
791f6f6
add swagger api docs
romulo-evangelista Mar 7, 2021
1d7a273
add tests
romulo-evangelista Mar 7, 2021
a5fffee
build interface pages with mock data
romulo-evangelista Mar 7, 2021
8cecc69
list and delete data from api
romulo-evangelista Mar 8, 2021
393a988
improve edit route
romulo-evangelista Mar 8, 2021
3d45412
improve form to add and edit professional/types
romulo-evangelista Mar 8, 2021
ac93b6a
feat: implement notifications
romulo-evangelista Mar 8, 2021
24c3552
feat: add pop confirmation before delete
romulo-evangelista Mar 8, 2021
7399808
feat: add docker and docker-compose
romulo-evangelista Mar 8, 2021
e446f06
fix: comment about DB_HOST if using docker
romulo-evangelista Mar 9, 2021
8fb4658
feat: add readme files and edit
romulo-evangelista Mar 9, 2021
54e654b
fix: readme text fixed
romulo-evangelista Mar 9, 2021
5f618cf
docs: add how execute in readme
romulo-evangelista Mar 10, 2021
25957f2
docs: edit docker tutorial to run app
romulo-evangelista Mar 10, 2021
c8b50bd
fix: using api base url from .env
romulo-evangelista Mar 10, 2021
437343e
feat: add redux + sagas
romulo-evangelista Mar 11, 2021
9a45b2a
refactor: create redux interfaces' folder
romulo-evangelista Mar 11, 2021
e8ec302
feat: init backend with pure js and implement routes
romulo-evangelista Mar 24, 2021
eb51899
feat: create controllers
romulo-evangelista Mar 24, 2021
21578f8
feat: create services
romulo-evangelista Mar 24, 2021
738da56
feat: improve professional types service
romulo-evangelista Mar 24, 2021
b0b6df4
feat: improve professional type controller
romulo-evangelista Mar 24, 2021
5d1ee84
feat: create professional type routes
romulo-evangelista Mar 24, 2021
5f8386f
feat: connect with database
romulo-evangelista Mar 25, 2021
eea82f6
feat: add .env.example
romulo-evangelista Mar 25, 2021
d03917e
feat: implements Professional model
romulo-evangelista Mar 25, 2021
f0f8f9e
feat: improve Professional Type Service
romulo-evangelista Mar 25, 2021
671763c
feat: improve Professional Service
romulo-evangelista Mar 25, 2021
94afaf4
feat: add swagger docs
romulo-evangelista Mar 26, 2021
fc238a2
feat: implements Professional tests
romulo-evangelista Mar 27, 2021
fc3c290
feat: implements Professional Type tests
romulo-evangelista Mar 27, 2021
4770156
refactor: remove unused imports
romulo-evangelista Mar 27, 2021
4c8e49a
feat: add .env.example
romulo-evangelista Mar 27, 2021
c2d8773
refactor: update readme.md
romulo-evangelista Mar 27, 2021
50ec6d3
feat: dockerizing app
romulo-evangelista Mar 28, 2021
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
backend-nestjs
README.md.old
190 changes: 74 additions & 116 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,117 +1,75 @@
![Logo](https://i.postimg.cc/qRXTZZ0z/logo-3.png)

- [Desafio FullStack :earth_americas: :computer: :iphone:](#desafio-fullstack-earth_americas-computer-iphone)
- [Contexto :game_die:](#contexto-game_die)
- [Modelo de dados :memo:](#modelo-de-dados-memo)
- [Tipo de profissional](#tipo-de-profissional)
- [Profissional](#profissional)
- [Requisitos funcionais :white_check_mark:](#requisitos-funcionais-white_check_mark)
- [Backend :earth_americas:](#backend-earth_americas)
- [Frontend :computer:](#frontend-computer)
- [Mobile :iphone:](#mobile-iphone)
- [Diferencial :crossed_swords:](#diferencial-crossed_swords)
- [Requisitos não funcionais :cool:](#requisitos-não-funcionais-cool)
- [Opcional - Requisitos avançados :grey_exclamation:](#opcional---requisitos-avançados-grey_exclamation)
- [Com o que nos preocupamos :dart:](#com-o-que-nos-preocupamos-dart)
- [FAQ :question:](#faq-question)

# Desafio FullStack :earth_americas: :computer: :iphone:

**Seja bem-vindo ao nosso desafio de desenvolvimento!** :raised_hands:

*Tenha certeza de ter lido todo o documento atentamente até o final e esclarecido as dúvidas com nosso time caso surja alguma.*

Esta é a proposta de um desafio que FullStack sinta-se a vontade para tentá-lo por completo, caso seu objetivo esteja focado em uma das duas funções Backend, Frontend ou Mobile fique a vontade para deixar o outros desafios de lado.

:rocket: Tudo certo!? Então vamos lá!


# Contexto :game_die:

Em todo aplicativo comercial temos um controle dos profissinais envolvidos no processo sejam usuários, responsáveis, gerentes, administradores, operadores, etc. Por isso um ponto importante de qualquer aplicação é permitir designarmos estas funções ou seja categorizar em tipos estes profissionais. Ex.: ProfissionalAna = Médica, José = Professor...

Vamos criar então uma aplicação que nos permita consultar, criar e editar essas informações e manter essa relação entre o profissional e seu tipo.

## Modelo de dados :memo:
### Tipo de profissional
```js
{
"id": xxx, // ID
"descricao": "test", // descricao do tipo *Obrigatório
"situacao": "true", // situacao do cadastro *Obrigatório
"updatedAt": "", // data e hora ultima atualizacao *Obrigatório
"createdAt": "" // data e hora de cadastro *Obrigatório
}
```

### Profissional
```js
{
"id": xxx, // ID
"nome": "teste", // Nome do profisisonal *Obrigatório
"telefone": "(xx) xxxx", // Telefone
"email": "[email protected]", // Endereço de e-mail do profissional
"tipoDeProfissional": xxx, // Vinculo com o tipo de profissional *Obrigatório
"situacao": true, // Situação do cadastro *Obrigatório
"updatedAt": "", // Data e hora da última atualização *Obrigatório
"createdAt": "" // Data e hora da de cadastro *Obrigatório
}
```

## Requisitos funcionais :white_check_mark:
### Backend :earth_americas:
- A API deve seguir as boas práticas e padrões de implementação REST
- Os dados deve ser salvos em um banco de dados
- Escrever os testes para o código e as APIs geradas
- Prover documentação para API. (Sugestão OpenAPI/Swagger)
- Use **Node.js** e qualquer outro framework
- Use **Javascript ECMA**
- Use qualquer DB. PostgreSQL DB é a sugestão, sinta-se livre para usar qualquer outro.

### Frontend :computer:
** Se optar por realizar somente desafio frontend, utilize o modelo de dados proposto com algum mecanismo para gerar o Mock dos dados.
- Criar uma tela home com menu de acesso as funcionalidades
- Uma tela de listagem para cada uma das entidades
- Uma tela de cadastro para cada uma das entidades
- Implementar solução usando **ReactJS** ultima versão disponível
- Fique a vontade para utilizar bibliotecas de componentes de mercado ou criar os seus
- Utilize **Javascript ECMA**

### Mobile :iphone:
- Também desenvolvemos soluções mobile e procuramos profissionais com essas habilidades e conhecimentos.
- Caso queira ou seja solicitado a você desenvolva a aplicação mobile com os mesmos requisitos do Frontend, usando tecnologia **ReactNative**.

## Diferencial :crossed_swords:
- Documentação clara do código. Código comentado sempre é bom!
- Boas mensagens de commit ajudam!

## Requisitos não funcionais :cool:
- Um arquivo README.md com o resumo de escolhas por frameworks, bibliotecas, banco de dados e como executar seu projeto.


## Opcional - Requisitos avançados :grey_exclamation:

Estes requisitos são opcionais no desafio, sinta-se a vontade para deixá-los de lado, a menos que seja solicitado que os cumpra!

- Criar mecanismo completo de autenticação e autorização (authentication/authorization/etc.) , como OAuth.
- Criar mecanismo de log e auditoria (quando/como/quem etc.).
- Configuração Docker para build da imagem do projeto, docker compose para subir banco de dados com carga inicial necessaria (migrations, seeders).

# Com o que nos preocupamos :dart:
- Primeiramente em deixar claro que o desfio não tem caráter de eliminação
- Com certeza muito mais do que o desafio completo é avaliarmos suas competências e habilidades até o ponto em que chegou.
- Sabemos que nem todos temos o mesmo tempo disponível, então como dissemos fique a vontade para ir até onde conseguir ou solicitar mais tempo para o processo, transparência total.
- Sinta-se livre pra usar bibliotecas de código aberto se fizerem sentido, e lembre que avaliaremos sua capacidade de resolver problemas reais.
- Procuramos por código funcional e limpo
- Exemplos práticos de conhecimento em NodeJS e Javascript ECMA e suas APIs padrões
- Orientação a testes

# FAQ :question:
> Como devo fazer a entrega do desafio?

- Faça um Fork deste repositório crie um branch de trabalho e nos mande o Pull Request. Depois disso é só atualizar com o link do seu Pull Request no formulário do processo seletivo.

> Se eu tiver dúvidas?
- Entre em contato com nosso time que esta te apoiando no processo seletivo ou pelo [email protected].
# Objetivo
Repositório destinado ao desafio proposto da Maxxidata
# Sobre o desenvolvimento
## Backend
### Escolhas
Para desenvolver o backend decidi utilizar NodeJS + Express pois já tenho uma certa familiaridade.

[Voltar ao topo](#desafio-fullstack-earth_americas-computer-iphone)
### Documentação
A documentação da API foi feita utilizando o *Swagger*. Para acessar a documentação, basta acessar o link `http://localhost:3333/api` quando a aplicação estiver em execução.

- Rotas
- Profissionais

![Rotas de Profissionais](readme-files/professional/routes.png)

- Profissões

![Rotas de Profissionais](readme-files/professional-type/routes.png)

- Schemas
- Profissionais

![Rotas de Profissionais](readme-files/professional/schema.png)

- Profissões

![Rotas de Profissionais](readme-files/professional-type/schema.png)

### Como executar?
Para executar o projeto:
- Ambiente de desenvolvimento
- Usando Yarn: `yarn start`

## Frontend
Para desenvolver o frontend decidi utilizar o [ReactJS](https://reactjs.org/) + [Ant Design](https://ant.design/) pois já tenho uma certa familiaridade.

- Componentes
- Tela de Início - `Home`
- Cabeçalho - `Header`
- Profissionais - `Professionals`
- Tipos de Profissional ou Profissões - `ProfessionalTypes`
- Profissional - Profissão (Criado apenas para mostrar a relação entre os dois componentes citados acima) - `ProfessionalProfessionalTypes`

### Telas
- Home

![Página inicial](readme-files/home.png)

- Listar profissionais

![Listar profissionais](readme-files/professional/list.png)

- Cadastrar profissional

![Novo profissional](readme-files/professional/add.png)

- Editar profissional

![Nova profissisão](readme-files/professional/edit.png)

- Listar profissões

![Listar profissões](readme-files/professional-type/list.png)

- Cadastrar profissão

![Novo profissional](readme-files/professional-type/add.png)

- Editar profissão

![Nova profissisão](readme-files/professional-type/edit.png)

### Como executar?
Para executar o projeto:
- Basta executar o comando: `yarn start`
6 changes: 6 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
PORT=

DB_HOST=
DB_USER=
DB_PASS=
DB_NAME=
2 changes: 2 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.env
11 changes: 11 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM node:12.13-alpine

WORKDIR /usr/src/app

COPY package*.json ./

RUN yarn

COPY . .

CMD ["yarn", "start"]
69 changes: 69 additions & 0 deletions backend/__tests__/ProfessionalController.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { valideProfessional } from './util';
import ProfessionalController from '../src/app/controllers/ProfessionalController';

const mockProfessional = {
list: jest.fn(() => []),
getOne: jest.fn((id) => valideProfessional()),
create: jest.fn((data) => valideProfessional()),
update: jest.fn((id, data) => { return { ...valideProfessional(),...data } }),
delete: jest.fn((id) => true),
}
jest.mock('../src/app/controllers/ProfessionalController', () => {
return jest.fn().mockImplementation(() => {
return mockProfessional;
});
});

jest.mock('../src/app/controllers/ProfessionalTypeController');

beforeEach(() => {
ProfessionalController.mockClear();
});

describe('Professional tests', () => {
it('should professional constructor is called', async () => {
const professional = ProfessionalController();
expect(professional).toBeTruthy();
expect(ProfessionalController).toHaveBeenCalledTimes(1);
});

it('should list of professionals', async () => {
const professional = ProfessionalController();
const list = [];
await professional.list();
expect(mockProfessional.list.mock.results[0].value).toEqual(list);
expect(mockProfessional.list).toHaveBeenCalledTimes(1);
});

it('should get one professional', async () => {
const professional = ProfessionalController();
const one = valideProfessional();
await professional.getOne(one.id);
expect(mockProfessional.getOne.mock.results[0].value).toEqual(one);
expect(mockProfessional.getOne).toHaveBeenCalledTimes(1);
});

it('should create a professional', async () => {
const professional = ProfessionalController();
const create = valideProfessional();
await professional.create(create);
expect(mockProfessional.create.mock.results[0].value).toEqual(create);
expect(mockProfessional.create).toHaveBeenCalledTimes(1);
});

it('should update a professional', async () => {
const professional = ProfessionalController();
const created = await professional.create(valideProfessional());
const updated = await professional.update(created.id, { name: 'Maria' });
expect(mockProfessional.update.mock.results[0].value).toEqual(updated);
expect(mockProfessional.update).toHaveBeenCalledTimes(1);
});

it('should delete a professional', async () => {
const professional = ProfessionalController();
const created = await professional.create(valideProfessional());
const deleted = await professional.delete(created.id);
expect(mockProfessional.delete.mock.results[0].value).toEqual(deleted);
expect(mockProfessional.delete).toHaveBeenCalledTimes(1);
});
});
73 changes: 73 additions & 0 deletions backend/__tests__/ProfessionalTypeController.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { valideProfessionalType } from './util';
import ProfessionalTypeController from '../src/app/controllers/ProfessionalTypeController';

const mockProfessionalType = {
list: jest.fn(() => []),
getOne: jest.fn((id) => valideProfessionalType()),
create: jest.fn((data) => valideProfessionalType()),
update: jest.fn((id, data) => {
return { ...valideProfessionalType(),...data }
}),
delete: jest.fn((id) => true),
}
jest.mock('../src/app/controllers/ProfessionalTypeController', () => {
return jest.fn().mockImplementation(() => {
return mockProfessionalType;
});
});

jest.mock('../src/app/controllers/ProfessionalTypeController');

beforeEach(() => {
ProfessionalTypeController.mockClear();
});

describe('Professional tests', () => {
it('should professional type constructor is called', async () => {
const professionalType = ProfessionalTypeController();
expect(professionalType).toBeTruthy();
expect(ProfessionalTypeController).toHaveBeenCalledTimes(1);
});

it('should list of professional types', async () => {
const professionalType = ProfessionalTypeController();
const list = [];
await professionalType.list();
expect(mockProfessionalType.list.mock.results[0].value).toEqual(list);
expect(mockProfessionalType.list).toHaveBeenCalledTimes(1);
});

it('should get one professional type', async () => {
const professionalType = ProfessionalTypeController();
const one = valideProfessionalType();
await professionalType.getOne(one.id);
expect(mockProfessionalType.getOne.mock.results[0].value).toEqual(one);
expect(mockProfessionalType.getOne).toHaveBeenCalledTimes(1);
});

it('should create a professional type', async () => {
const professionalType = ProfessionalTypeController();
const create = valideProfessionalType();
await professionalType.create(create);
expect(mockProfessionalType.create.mock.results[0].value).toEqual(create);
expect(mockProfessionalType.create).toHaveBeenCalledTimes(1);
});

it('should update a professional type', async () => {
const professionalType = ProfessionalTypeController();
const created = await professionalType.create(valideProfessionalType());
const updated = await professionalType.update(
created.id, { description: 'new description' }
);
expect(mockProfessionalType.update.mock.results[0].value).toEqual(updated);
expect(mockProfessionalType.update).toHaveBeenCalledTimes(1);
});

it('should delete a professional type', async () => {
const professionalType = ProfessionalTypeController();
const created = await professionalType.create(valideProfessionalType());
const deleted = await professionalType.delete(created.id);
expect(mockProfessionalType.delete.mock.results[0].value).toEqual(deleted);
expect(mockProfessionalType.delete).toHaveBeenCalledTimes(1);
});
});
18 changes: 18 additions & 0 deletions backend/__tests__/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const valideProfessionalType = () => {
return {
id: 1,
description: 'this is a description',
situation: true,
}
}

export const valideProfessional = (professionalTypeId) => {
return {
id: 1,
name: 'Jonh',
phone: '(00) 0 0000-0000',
email: '[email protected]',
professionalTypeId: professionalTypeId,
situation: true,
}
}
4 changes: 4 additions & 0 deletions backend/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// babel.config.js
module.exports = {
presets: [['@babel/preset-env', {targets: {node: 'current'}}]],
};
Loading