Este um projeto de uma API REST para gerenciar candidatos, desenvolvido como parte do teste da Maxxmobi. A API permite realizar operaes CRUD (Create, Read, Update, Delete) em candidatos.
As dependncias do projeto so gerenciadas atravs do Maven. Abaixo est um resumo das principais dependncias utilizadas:
A aplicao est configurada para utilizar MySQL em desenvolvimento local e PostgreSQL em produo no Render.
No arquivo src/main/resources/application-dev.properties
:
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=mysql
spring.datasource.url=jdbc:mysql://localhost/db_maxxmobi?createDatabaseIfNotExist=true&serverTimezone=America/Sao_Paulo&useSSl=false
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
spring.jpa.properties.jakarta.persistence.sharedCache.mode=ENABLE_SELECTIVE
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=Brazil/East
No arquivo src/main/resources/application-pro.properties
:
spring.jpa.generate-ddl=true
spring.jpa.database=postgresql
spring.datasource.url=jdbc:postgresql://${POSTGRESHOST}:${POSTGRESPORT}/${POSTGRESDATABASE}
spring.datasource.username=${POSTGRESUSER}
spring.datasource.password=${POSTGRESPASSWORD}
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.jakarta.persistence.sharedCache.mode=ENABLE_SELECTIVE
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=Brazil/East
No arquivo src/main/resources/application.properties
:
spring.profiles.active=prod
springdoc.api-docs.path=/v3/api-docs
springdoc.swagger-ui.path=/swagger-ui.html
springdoc.swagger-ui.operationsSorter=method
springdoc.swagger-ui.disable-swagger-default-url=true
springdoc.swagger-ui.use-root-path=true
springdoc.packagesToScan=br.com.maxxmobi.teste.controller
Este projeto inclui um Dockerfile para facilitar a criao de contineres Docker. O Dockerfile configurado para construir e rodar a aplicao em produo com OpenJDK 17.0.1.
FROM openjdk:17.0.1-jdk-oracle as build
WORKDIR /workspace/app
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY src src
RUN chmod -R 777 ./mvnw
RUN ./mvnw install -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
FROM openjdk:17.0.1-jdk-oracle
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","br.com.maxxmobi.teste.ApiRestFullCantidatosApplication"]
Clone o repositrio:
git clone <URL_DO_REPOSITORIO>
cd <NOME_DO_PROJETO>
Configure o banco de dados conforme descrito acima.
Execute o projeto usando Maven:
mvn spring-boot:run
A documentao da API est disponvel no Swagger. Aps iniciar a aplicao, acesse a seguinte URL para visualizar a documentao:
http://localhost:8080/swagger-ui.html
Para acessar a documentao da API hospedada na plataforma Render, utilize a URL:
https://projeto-teste-maxxmobi.onrender.com/swagger-ui.html
Para acessar a API, utilize as seguintes credenciais:
[email protected]
admin123
Para acessar a API localmente devemos criar um novo usurio, utilize o Insomnia ou outra ferramenta similar para fazer uma requisio POST para a URL:
http://localhost:8080/usuarios/cadastrar
A segurana da aplicao gerenciada pelo Spring Security. A autenticao e autorizao so feitas atravs de tokens JWT.
Os testes unitrios e de integrao foram realizados utilizando JUnit. Para executar os testes, utilize o comando:
mvn test
Foram realizados testes para garantir o funcionamento correto dos endpoints relacionados aos usurios. A seguir, esto descritos os testes realizados com o UsuarioController
:
@Test
@DisplayName("Cadastrar um Usurio")
public void deveCriarUmUsuario() {
HttpEntity<Usuario> corpoRequisicao = new HttpEntity<Usuario>(new Usuario(0L, "Edvaldo Junior", "[email protected]", "12345678"));
ResponseEntity<Usuario> corpoResposta = testRestTemplate
.exchange("/usuarios/cadastrar", HttpMethod.POST, corpoRequisicao, Usuario.class);
assertEquals(HttpStatus.CREATED, corpoResposta.getStatusCode());
}
Descrio: Testa a criao de um novo usurio.
Expectativa: O status HTTP retornado deve ser 201 Created
.
@Test
@DisplayName("No deve permitir duplicao do Usurio")
public void naoDeveDuplicarUsuario() {
usuarioService.cadastrarUsuario(new Usuario(0L, "Maria da Silva", "[email protected]", "12345678"));
HttpEntity<Usuario> corpoRequisicao = new HttpEntity<Usuario>(new Usuario(0L, "Maria da Silva", "[email protected]", "12345678"));
ResponseEntity<Usuario> corpoResposta = testRestTemplate
.exchange("/usuarios/cadastrar", HttpMethod.POST, corpoRequisicao, Usuario.class);
assertEquals(HttpStatus.BAD_REQUEST, corpoResposta.getStatusCode());
}
Descrio: Testa a tentativa de cadastro de um usurio com dados duplicados.
Expectativa: O status HTTP retornado deve ser 400 Bad Request
.
@Test
@DisplayName("Atualizar um Usurio")
public void deveAtualizarUmUsuario() {
Optional<Usuario> usuarioCadastrado = usuarioService.cadastrarUsuario(new Usuario(0L, "Juliana Andrews", "[email protected]", "juliana123"));
Usuario usuarioUpdate = new Usuario((usuarioCadastrado.get().getId()),
"Juliana Andrews", "[email protected]", "juliana123");
HttpEntity<Usuario> corpoRequisicao = new HttpEntity<Usuario>(usuarioUpdate);
ResponseEntity<Usuario> corpoResposta = testRestTemplate
.withBasicAuth("[email protected]", "rootroot")
.exchange("/usuarios/atualizar", HttpMethod.PUT, corpoRequisicao, Usuario.class);
assertEquals(HttpStatus.OK, corpoResposta.getStatusCode());
}
Descrio: Testa a atualizao dos dados de um usurio existente.
Expectativa: O status HTTP retornado deve ser 200 OK
.
@Test
@DisplayName("Listar todos os Usurios")
public void deveMostrarTodosUsuarios() {
usuarioService.cadastrarUsuario(new Usuario(0L, "Sabrina Sanches", "[email protected]", "sabrina123"));
usuarioService.cadastrarUsuario(new Usuario(0L, "Ricardo Marques", "[email protected]", "ricardo123"));
ResponseEntity<String> resposta = testRestTemplate
.withBasicAuth("[email protected]", "rootroot")
.exchange("/usuarios/all", HttpMethod.GET, null, String.class);
assertEquals(HttpStatus.OK, resposta.getStatusCode());
}
200 OK
.SpringBootTest.WebEnvironment.RANDOM_PORT
).Esses testes garantem que o controle de usurios est funcionando conforme o esperado e validam o comportamento da aplicao em cenrios tpicos de operao.
Abaixo esto os cdigos SQL que correspondem criao da tabela tb_candidatos
baseada na entidade CandidadosModel
:
CREATE TABLE tb_candidatos (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
nascimento DATE NOT NULL,
data_criacao TIMESTAMP NOT NULL,
sexo CHAR(1) DEFAULT 'M' NOT NULL,
nota INT NOT NULL,
logradouro VARCHAR(200),
bairro VARCHAR(50),
cidade VARCHAR(50),
uf CHAR(2)
);
DATE
.TIMESTAMP
.CHAR(1)
.INT
.VARCHAR(200)
.VARCHAR(50)
.VARCHAR(50)
.CHAR(2)
.sexo
tem um valor padro definido como 'M' (Masculino).logradouro
, bairro
, cidade
, e uf
so opcionais e podem ser NULL
.Usuario
Aqui esto os cdigos SQL que correspondem criao da tabela tb_usuarios
baseada na entidade Usuario
:
CREATE TABLE tb_usuarios (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(255) NOT NULL,
usuario VARCHAR(255) NOT NULL UNIQUE,
senha VARCHAR(255) NOT NULL
);
nome
, usuario
, e senha
so obrigatrios e no podem ser nulos.usuario
deve ser nico e deve seguir o formato de um email vlido.Caso queira verificar os cdigos SQL gerados, voc pode habilitar a exibio de SQL no Hibernate atravs da configurao spring.jpa.show-sql=true
no arquivo application.properties
.
A estrutura de pastas apresenta uma organizao comum em projetos Spring Boot, com as seguintes responsabilidades:
config.SwaggerConfig:
Configura a gerao da documentao da API utilizando o Swagger.
controller.CandidatoController:
Controla as requisies relacionadas a candidatos, como listar, criar, atualizar e excluir candidatos.
controller.UsuarioController:
Controla as requisies relacionadas a usurios, como login, cadastro e gerenciamento de perfis.
model.CandidatosModel:
Representa um candidato, com atributos como nome, email, etc.
model.Usuario:
Representa um usurio do sistema, com atributos como nome de usurio, senha, etc.
model.UsuarioLogin:
Pode ser utilizado para representar os dados de login de um usurio (nome de usurio e senha).
repository.CandidatosRepository:
Interface que define os mtodos para acessar e manipular os dados de candidatos no banco de dados.
repository.UsuarioRepository:
Interface que define os mtodos para acessar e manipular os dados de usurios no banco de dados.
security.BasicSecurityConfig:
Configura a segurana bsica da aplicao, como autenticao por basic authentication.
security.JwtAuthFilter:
Filtro que realiza a autenticao utilizando tokens JWT.
security.JwtService:
Servio responsvel por gerar, validar e armazenar tokens JWT.
security.UserDetailsImpl:
Implementao da interface UserDetails, que representa um usurio autenticado.
security.UserDetailsServiceImpl:
Servio responsvel por carregar os detalhes do usurio a partir do banco de dados.
service:
Responsvel por gerenciar usurios do sistema. Ela prov mtodos para cadastro.
service.ApiRestFullCandidatosApplication:
Classe principal da aplicao Spring Boot.
GET /usuarios/{id}
Objetivo: Buscar um usurio especfico pelo seu ID.
Exemplo de Requisio:
GET /usuarios/1
Exemplo de Resposta (Sucesso):
{
"id": 1,
"nome": "Joo da Silva",
"email": "[email protected]"
// ... outros atributos do usurio
}
Exemplo de Resposta (Erro):
{
"mensagem": "Usurio no encontrado"
}
Objetivo: Listar todos os usurios cadastrados.
Exemplo de Requisio:
GET /usuarios/all
Exemplo de Resposta (Sucesso):
[
{
"id": 1,
"nome": "Joo da Silva",
"email": "[email protected]"
},
{
"id": 2,
"nome": "Maria da Silva",
"email": "[email protected]"
}
// ... outros usurios
]
Objetivo: Realizar o login de um usurio.
Exemplo de Requisio:
{
"email": "[email protected]",
"senha": "minhaSenha123"
}
Exemplo de Resposta (Sucesso):
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqb2FvQGVtYWlsLmNvbSIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNjYxNTE2NTEyLCJleHAiOjE2NjE1MjAzMTJ9.yJmM1O854aGzvZ3X-ywOo97b0nmTvjNuuo6qwU7AiM"
}
Exemplo de Resposta (Erro):
{
"mensagem": "Credenciais invlidas"
}
Objetivo: Cadastrar um novo usurio.
Exemplo de Requisio:
{
"nome": "Novo Usurio",
"email": "[email protected]",
"senha": "minhaNovaSenha"
}
Exemplo de Resposta (Sucesso):
{
"mensagem": "Usurio cadastrado com sucesso"
}
Exemplo de Resposta (Erro):
{
"mensagem": "Email j cadastrado"
}
Objetivo: Atualizar os dados de um usurio existente.
Exemplo de Requisio:
{
"id": 1,
"nome": "Joo da Silva Atualizado",
"email": "[email protected]"
}
Exemplo de Resposta (Sucesso):
{
"mensagem": "Usurio atualizado com sucesso"
}
Exemplo de Resposta (Erro):
{
"mensagem": "Usurio no encontrado"
}
Descrio: Exclui um candidato especfico, identificado pelo seu ID.
Requisio:
DELETE /candidatos/1
Resposta (Sucesso):
{
"message": "Candidato excludo com sucesso."
}
Resposta (Erro, por exemplo, candidato no encontrado):
{
"error": "Candidato no encontrado."
}
Descrio: Obtm os dados de um candidato especfico, identificado pelo seu ID.
Requisio:
GET /candidatos/1
Resposta (Sucesso):
{
"id": 1,
"nome": "Marina Barros",
"nascimento": "2003-10-10",
"dataCriacao": "2024-07-25",
"sexo": "F",
"nota": 9,
"logradouro": "Avenida dos Ips",
"bairro": "Ips",
"cidade": "Manaus",
"uf": "AM"
}
Resposta (Erro, por exemplo, candidato no encontrado):
{
"error": "Candidato no encontrado."
}
Descrio: Busca candidatos por sexo.
Requisio:
GET /candidatos/buscarPorSexo?sexo=F
Resposta (Sucesso):
[
{
"id": 1,
"nome": "Marina Barros",
"nascimento": "2003-10-10",
"dataCriacao": "2024-07-25",
"sexo": "F",
"nota": 9,
"logradouro": "Avenida dos Ips",
"bairro": "Ips",
"cidade": "Manaus",
"uf": "AM"
},
{
"id": 2,
"nome": "Ana Souza",
"nascimento": "1999-05-22",
"dataCriacao": "2024-07-25",
"sexo": "F",
"nota": 8,
"logradouro": "Rua das Flores",
"bairro": "Jardim",
"cidade": "So Paulo",
"uf": "SP"
}
]
Resposta (Erro, por exemplo, parmetro invlido):
{
"error": "Sexo invlido. Use 'M' para masculino ou 'F' para feminino."
}
Descrio: Busca candidatos por nota.
Requisio:
GET /candidatos/buscarPorNota?nota=9
Resposta (Sucesso):
[
{
"id": 1,
"nome": "Marina Barros",
"nascimento": "2003-10-10",
"dataCriacao": "2024-07-25",
"sexo": "F",
"nota": 9,
"logradouro": "Avenida dos Ips",
"bairro": "Ips",
"cidade": "Manaus",
"uf": "AM"
}
]
Resposta (Erro, por exemplo, parmetro invlido):
{
"error": "Nota invlida. Deve ser um nmero inteiro."
}
Descrio: Busca candidatos por nome.
Requisio:
GET /candidatos/buscarPorNome?nome=Marina
Resposta (Sucesso):
[
{
"id": 1,
"nome": "Marina Barros",
"nascimento": "2003-10-10",
"dataCriacao": "2024-07-25",
"sexo": "F",
"nota": 9,
"logradouro": "Avenida dos Ips",
"bairro": "Ips",
"cidade": "Manaus",
"uf": "AM"
}
]
Resposta (Erro, por exemplo, nome no encontrado):
{
"error": "Nenhum candidato encontrado com o nome fornecido."
}
Descrio: Busca candidatos por data de nascimento.
Requisio:
GET /candidatos/buscarPorNascimento?nascimento=2003-10-10
Resposta (Sucesso):
[
{
"id": 1,
"nome": "Marina Barros",
"nascimento": "2003-10-10",
"dataCriacao": "2024-07-25",
"sexo": "F",
"nota": 9,
"logradouro": "Avenida dos Ips",
"bairro": "Ips",
"cidade": "Manaus",
"uf": "AM"
}
]
Resposta (Erro, por exemplo, data invlida):
{
"error": "Data de nascimento invlida. Use o formato YYYY-MM-DD."
}
Descrio: Busca todos os candidatos de forma ordenada (o critrio de ordenao deve ser especificado com ASC ou DESC).
Requisio:
GET /candidatos/busca/ordenada?ordem=nome
Resposta (Sucesso):
[
{
"id": 2,
"nome": "Ana Souza",
"nascimento": "1999-05-22",
"dataCriacao": "2024-07-25",
"sexo": "F",
"nota": 8,
"logradouro": "Rua das Flores",
"bairro": "Jardim",
"cidade": "So Paulo",
"uf": "SP"
},
{
"id": 1,
"nome": "Marina Barros",
"nascimento": "2003-10-10",
"dataCriacao": "2024-07-25",
"sexo": "F",
"nota": 9,
"logradouro": "Avenida dos Ips",
"bairro": "Ips",
"cidade": "Manaus",
"uf": "AM"
}
]
Resposta (Erro, por exemplo, critrio de ordenao invlido):
{
"error": "Critrio de ordenao invlido. Use 'nome', 'nota', etc."
}
Descrio: Cria um novo candidato.
Requisio:
POST /candidatos
Corpo da Requisio:
{
"nome": "Marina Barros",
"nascimento": "2003-10-10",
"dataCriacao": "2024-07-25",
"sexo": "F",
"nota": 9,
"logradouro": "Avenida dos Ips",
"bairro": "Ips",
"cidade": "Manaus",
"uf": "AM"
}
Resposta (Sucesso):
{
"message": "Candidato criado com sucesso.",
"id": 3
}
Resposta (Erro, por exemplo, dados invlidos):
jsonCopiar cdigo{
"error": "Dados invlidos. Verifique os campos e tente novamente."
}
Descrio: Atualiza os dados de um candidato (provavelmente, o ID enviado no corpo da requisio).
Requisio:
PUT /candidatos
Corpo da Requisio:
{
"id": 1,
"nome": "Marina Barros",
"nascimento": "2003-10-10",
"dataCriacao": "2024-07-25",
"sexo": "F",
"nota": 10,
"logradouro": "Avenida dos Ips",
"bairro": "Ips",
"cidade": "Manaus",
"uf": "AM"
}
Resposta (Sucesso):
{
"message": "Candidato atualizado com sucesso."
}
Resposta (Erro, por exemplo, candidato no encontrado):
{
"error": "Candidato no encontrado. Verifique o ID e tente novamente."
}
https://github.com/edvaldoljr/Projeto-Teste-Maxxmobi/blob/main/Projeto%20Teste%20Maxxmobi.pdf
Prezado Altamiro Santos
Agradeo pela oportunidade de participar do processo seletivo para a vaga de desenvolvedor Java jnior. Estou entregando o teste conforme solicitado e estou disposio para quaisquer esclarecimentos ou dvidas que possam surgir.
Espero que o trabalho realizado esteja alinhado com o que vocs esto buscando. Agradeo a sua considerao e aguardo ansiosamente o retorno.
Atenciosamente, Edvaldo Junior