Fiz uma pesquisa de soluções que o ITAU e vi que em seu dia-a-dia o mesmo faz uso do Spring Boot e para manter dentro dos padrões decidi utiliza-lo.
Tomei a decisão de separar as camadas da aplicação em Aplication e Domain. No qual a camada de Application é responsável por manter tudo que for do contexto da aplicação, sendo para este projeto a exception e rota. Já a camada Domain ficará responsável pela regra de negócio como visto na bibliografia DDD Domain Driven Design de Martin Fowler.
Para extrair o que temos de melhor decidi manter o projeto extremamente enxuto com a menor quantidade possível de libs, assim garantindo sua performance e facilidade de execução do mesmo além de fazer uso somente de libs oficiais como "lombok".
Para as validações decidi criar annotation para um possível reaproveitamento para outras partes do projeto em qualquer Object Value ou DTO.
Busquei manter o código legivel e desaclopado, também busquei retornar como response do request um Array de erros para que o front ou qualquer outro consumidor da API possa fazer uso do mesmo e obter um feedback do que precisamos para que as requisições da senha sejam cumpridas.
Para baixar o projeto o projeto na maquina faça uso do github
$ git clone git@github.com:endsalone/backend-challenge.git
Para executar o projeto basta executar no terminal o seguinte comando dentro da pasta raiz em que o projeto foi baixado:
$ ./mvnw clean package spring-boot:repackage
$ java -jar target/password-validation-0.0.1-SNAPSHOT.jar
Após rodar o projeto para fazer as requisições basta seguir o contrato abaixo
POST /api/passwords/validation HTTP/1.1
Host: localhost:8080
Content-Type: application/json
{
"password": "AbTp9!fok"
}
Response de sucesso esperado
POST /api/passwords/validation HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Status Code: 200
Response:
true
Response de erro esperado
POST /api/passwords/validation HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Status Code: 400
Response:
{
"code": 400,
"errors": [
"A senha deve conter ao menos um caractere especial",
"O campo senha deve ser preenchido",
"A senha deve conter ao menos uma letra minuscula",
"A senha não deve conter caracteres repetidos",
"A senha deve conter ao menos uma letra maiúscula"
]
}
- Todos os requisitos contemplados
Request:
curl --location --request POST 'http://localhost:8080/api/passwords/validation' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=B79B8E1AF094E155B7FBBE2A8C4CD79C' \
--data-raw '{
"password": "AbTp9!fok"
}'
Response:
true
- Senha vazia
Request:
curl --location --request POST 'http://localhost:8080/api/passwords/validation' \
--header 'Content-Type: application/json' \
--data-raw '{}'
Response:
{
"code": 400,
"errors": [
"A senha deve conter ao menos um digito",
"A senha deve conter ao menos 9 caracteres",
"A senha deve conter ao menos um caractere especial",
"O campo senha deve ser preenchido",
"A senha deve conter ao menos uma letra minuscula",
"A senha não deve conter caracteres repetidos",
"A senha deve conter ao menos uma letra maiúscula"
]
}
- Nove ou mais caracteres
Request:
curl --location --request POST 'http://localhost:8080/api/passwords/validation' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=B79B8E1AF094E155B7FBBE2A8C4CD79C' \
--data-raw '{
"password": "Ab9!f"
}'
Response:
{
"code": 400,
"errors": [
"A senha deve conter ao menos 9 caracteres"
]
}
- Ao menos 1 letra minúscula
Request:
curl --location --request POST 'http://localhost:8080/api/passwords/validation' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=B79B8E1AF094E155B7FBBE2A8C4CD79C' \
--data-raw '{
"password": "ABCDEFG9!HI"
}'
Response:
{
"code": 400,
"errors": [
"A senha deve conter ao menos uma letra minuscula"
]
}
- Ao menos 1 letra maiúscula
Request:
curl --location --request POST 'http://localhost:8080/api/passwords/validation' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=B79B8E1AF094E155B7FBBE2A8C4CD79C' \
--data-raw '{
"password": "AbTpv!fok"
}'
Response:
{
"code": 400,
"errors": [
"A senha deve conter ao menos uma letra maiúscula"
]
}
- Ao menos 1 dígito
Request:
curl --location --request POST 'http://localhost:8080/api/passwords/validation' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=B79B8E1AF094E155B7FBBE2A8C4CD79C' \
--data-raw '{
"password": "AbTpv!fok"
}'
Response:
{
"code": 400,
"errors": [
"A senha deve conter ao menos um digito"
]
}
- Ao menos 1 caractere especial
Request:
curl --location --request POST 'http://localhost:8080/api/passwords/validation' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=B79B8E1AF094E155B7FBBE2A8C4CD79C' \
--data-raw '{
"password": "AbTpv0fok"
}'
Response:
{
"code": 400,
"errors": [
"A senha deve conter ao menos um caractere especial"
]
}
- Não possuir caracteres repetidos dentro do conjunto
Request:
curl --location --request POST 'http://localhost:8080/api/passwords/validation' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=B79B8E1AF094E155B7FBBE2A8C4CD79C' \
--data-raw '{
"password": "AbTpv0!ook"
}'
Response:
{
"code": 400,
"errors": [
"A senha não deve conter caracteres repetidos"
]
}
- Espaços em branco não devem ser considerados como caracteres válidos
Request:
curl --location --request POST 'http://localhost:8080/api/passwords/validation' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=B79B8E1AF094E155B7FBBE2A8C4CD79C' \
--data-raw '{
"password": "AbTpv0! ok"
}'
Response:
{
"code": 400,
"errors": [
"A senha não deve conter espaços"
]
}
Todos os testes Unitários e de Integração estão dentro da pasta de test. Optei por criar os testes de integração contemplando todos os possíveis cenários a fim de garantir a integridade do contrato de response.