Repositório para entrega do Desafio API (semana 18/07 ~ 29/07/2022) - Programa GFT Start
Objetivo: Aplicar os conceitos de API aprendidos nas semanas de estudo, juntamente com a pesquisa de novos recursos;
Escopo: Neste desafio você deverá desenvolver diversos End Points para um sistema de atendimento veterinário, nesse sistema, deverá considerar somente o atendimento de cachorros.
Entrega Básica:
O sistema deverá obrigatoriamente fornecer End Points para:
- CRUD completo para clientes (armazenar somente dados básicos)
- CRUD completo para médicos veterinários (armazenar somente dados básicos)
- CRUD completo para o cachorro (cada animal deverá estar associado a um tutor/cliente | dados básicos do animal)
- Registrar dados do atendimento: veterinário que está atendendo, tutor, animal em atendimento, data, hora, dados do animal no dia, diagnóstico, comentários.
- Autenticação do usuário (Basic Authentication – user:password) para acessar os End Points.
- Não deixe de popular o banco
Desafios bônus (Exceeds):
- No cadastro do animal, recuperar dados da raça buscando na API fornecida pelo link no final da descrição da atividade.
- Fornecer ao menos 3 serviços da API descrita no final da atividade para o cliente ( o cliente não pode acessar a API diretamente, deve acessar seu End Point, e seu End Poit deverá acessar a API e passar os dados para o cliente)
- Manter histórico dos atendimentos e permitir que tanto o cliente quanto veterinário possam acessar esses dados. OBS: Nesse caso, o cliente poderá acessar somente dados das consultas de seus animais. Os veterinários por sua vez, podem ver histórico de qualquer consulta.
- Swagger ou projeto no postman
- TDD
O link da API que deverá ser utilizada é apresentado a seguir: https://thedogapi.com/
Para o desafio foi criado Meu AmigAU! - Clínica Veterinária Especializada. Neste projeto foi aplicado:
- Os arquivos fontes dos diagramas EER e UML que aparecem como imagem neste documento estão disponíveis na pasta
docs/diagrams
. O diagrama EER foi feito utilizando o MySQL Workbench Model e o UML foi feito utilizando o site Draw.io; - Abstração criando
Person
para as classesClient
eVet
. Assim é possível que uma pessoa seja cadastrada como cliente e veterinário sem necessitar colocar dados cadastrais pessoais, como o CPF/CNPJ, data de nascimento ou o endereço novamente, e pode ser aproveitada evoluções do projeto, como cadastro de funcionários; - Criado a entidade
Address
para que a entidadePerson
possa ter mais de um endereço cadastrado; - Criado o enumerador
AddressType
para diferenciar os tipos de endereços que são vinculados à pessoa; - Criado o enumerador
PersonType
para que tenha a possibilidade de informar pessoa física ou jurídica. Este enumerador também auxilia na validação de CPF ou CNPJ na mesma variável, usando como apoio as interfacesCpfGroup
eCnpjGroup
e a classePersonGroupSequenceProvider
, conforme orientações da página Validando CPF/CNPJ na mesma variável; - O usuário, entidade
User
, pode possuir mais de um perfil de acesso; - Criada a classe
Breed
para guardar dados da raça com campos baseados na página TheDogAPI Postman Docs; - Utilizado o Swagger v3 (OpenApi) e na segurança foi usado o bean
SecurityFilterChain
para não utilizar oWebSecurityConfigurerAdapter
que foi depreciado nas versões recentes do spring; - Foram criadas classes com constants (
public static final
), pacoteenums.constants
, para guardar textos usados para mapeamentos de rotas, para documentação no swagger e em mensagens do sistema, assim é possível reaproveitá-las nas classes do sistema e testes, além de organizar os textos para facilitar revisão ou mesmo tradução; - Para evitar expor senhas ou chaves em repositório remoto, foram utilizadas variáveis de ambiente (environment variables) configuradas na IDE Ecplise, mais detalhes estarão na seção Installation;
- Criado o arquivo
application-test.properties
para que nos testes carregue o banco de dados H2, desativado o flyway e outros detalhes para evitar problemas com testes, dados da aplicação e problema com variáveis de ambientes em testes; - Criado a classe
DateTimeConverter
no pacotecom.gft.meuamigau.utils
para fazer a conversão de datas para string e vice-versa e ser utilizada em método estáticos (static
), ele utilizaDateTimeFormatter
eLocalDate
, pois oSimpleDateFormatter
, que faria uma conversão mais direta deDate
paraString
, e vice-versa, não é thread-safe e, portanto, utilizá-lo em métodos estáticos pode acarretar em problemas, ver mais sobre; - Para nomes de variáveis diferentes entre objeto e JSON, foi utilizado as intruções em Serialize POJO to JSON with different names using GSON;
- Para os testes, foi criado o método estático público
assertThrowsExceptionWithCorrectMessage
na classeAssertions
, pacotecom.gft.meuamigau.utils
, para abstrair a conferência do lançamento de excessão com mensagem correta; - Criado a classe
EntityDtoBox
, pacoteutils
, para poder fazer um mesmo método poder retornar entidade ou DTO. Esta classe foi utilizada em métodos create e findById dos services; - Os endpoints do grupo "Raças de Cachorros" fazem busca na API externa TheDogAPI. Apenas o endpoint http://localhost:8080/api/v1/breeds/{id} que realiza primeiramente uma consulta na base de dados local e quando não localiza a raça cadastrada, ele procura na base externa e salva os dados;
- Todo cadastro de cachorro que inclui um id de raça que ainda não existe na base de dados local, já aproveita o método de busca no endpoint http://localhost:8080/api/v1/breeds/{id}, e salva as informações da raça localmente;
- No atendimento existe o atributo de cliente explícito, pois caso ocorrer a mudança de cliente no cachorro o atendimento ainda terá vinculo com o cadastro de cliente que era o responsável pelo cachorro no momento do atendimento.
O projeto foi feito utilizando:
- IDE Eclipse versão 2022-06 (4.24.0).
- Iniciado com Spring Starter Project, com as configurações e dependências:
- Project: Maven Project
- Language: Java
- Spring Boot 2.7.1
- Packaging: Jar
- Java 17
- Dependencies:
- Spring Boot DevTools
- Spring Web
- Spring Data JPA
- Spring Security
- Validation
- MySQL Driver
- Flyway
- Lombok
- H2 Database
- Dependências adicionadas manualmente ao
pom.xml
:springdoc-openapi-ui
espringdoc-openapi-security
para Swagger v3 (OpenApi);java-jwt
para trabalhar com token JWT;gson
para Converter JSON para Objeto com GSON;jacoco-maven-plugin
para gerar relatório de coverage quando o teste é executado diretamente pelo terminal. É necessário também inserir plugin específico, verifique opom.xml
para checar o pluginjacoco-maven-plugin
adicionado
Logotipo feito utilizando o site Canva:
Spring Banner personalizado com utilização de conversão de imagem para ascii pelo site ASCII-Art:
Diagrama EER com MySQL Workbench Model:
Diagrama de classes UML feito utilizando o site Draw.io:
Variáveis de ambiente usadas no application.properties
:
Swagger v3 - OpenApi:
Swagger Schemas - Cadastro de Pessoas - No Schema do RecordPersonDTO é mostrado os campos para requisição, cada um sua descrição e marcado com * se é requerido, no caso de uso de enumerador, é mostrada as opções disponíveis para o campo
Resposta de cadastro de pessoa no Swagger, é mostrado também os outros tipos de respostas que também podem ocorrer, como o 409 que indica violação de integridade em algum campo
Resposta de cadastro de pessoa com violação de integridade ao tentar cadastrar nova pessoa com Email já existente:
Classificação de Endpoints no Swagger - Tags por assunto:
Classificação de Endpoints no Swagger - Tags por autorização de acesso:
Classificação de Endpoints no Swagger - Tags por método HTTP:
Resultado dos testes na IDE Eclipse:
Resultado dos testes pelo terminal:
Relatório de coverage gerado pelo jacoco plugin:
Para abrir o projeto basta clonar o repositório ou realizar o download e após:
- Importar o projeto, preferencialmente na IDE Eclipse;
- Fazer atualizações das dependências do Maven (Alt+F5 no Eclipse);
- Possuir no mínimo JDK 17 LTS instalado, sugestão de JDK: OpenJDK Zulu;
- Possuir acesso à internet para atualização de dependências e acesso à API de terceiros;
- Para projeto carregar corretamente na IDE pode ser necessário Instalar Lombok;
- Necessário possuir o banco de dados MySQL Server, caso não possua, pode visitar MySQL Community Download para download e instalação;
- No arquivo src/main/resources/application.properties verificar e se necessário alterar os parâmentros conforme segue:
- URL de conexão com o banco:
spring.datasource.url=jdbc:mysql://localhost:3306/meu-amigau?createDatabaseIfNotExist=True
- Usuário root do banco:
spring.datasource.username=root
- O usuário root padrão com permissão ao MySQL Server local, alterar somente caso seja esteja desativado ou seja diferente; - Usuário root do banco:
spring.datasource.password=${DATABASE_ROOT_PASSWORD}
- Senha do banco de dados, alterar para a senha do root utilizado localmente ou setar valor na variável de ambienteDATABASE_ROOT_PASSWORD
; - Chave secreta para o JWT gerar e validar tokens:
meu-amigau-api.jwt.secret=${TOKEN_API_SECRET}
- Alterar para chave secreta de sua preferencia ou setar valor na variável de ambienteTOKEN_API_SECRET
. - Chave The Dog Api:
meu-amigau-api.the-dog-api.x-api-key=${THE_DOG_API_X_API_KEY}
- Alterar para chave gerada no site TheDogAPI ou setar o valor da chave na variável de ambienteTHE_DOG_API_X_API_KEY
.
- URL de conexão com o banco:
- Variáveis de ambientes que devem ser setadas no sistema ou na IDE para funcionamento adequado da aplicação:
- DATABASE_ROOT_PASSWORD
- TOKEN_API_SECRET
- THE_DOG_API_X_API_KEY
- Setar valor de variável de ambiente na IDE Eclipse:
Os testes foram feitos utilizando o JUnit 5, Mockito e MockMVC.
-
Utilizando a IDE Eclipse, basta executar (Run As) a pasta de teste ou escolher o arquivo.
-
Utilizando o terminal (PowerShell ou similiar), basta executar na pasta do projeto o comando abaixo:
./mvnw clean test
Após o teste finalizado com sucesso, é possível verificar relatório de coverage em: target/site/jacoco/index.html
Observação: Até a versão 1.0.0 existe teste na classe BreedServiceTest
, pacote services
, que faz conexão ao TheDogAPI, portanto para o teste executar é necessário estar online.
Antes de iniciar o projeto, configurar as variáveis de ambiente DATABASE_ROOT_PASSWORD
, TOKEN_API_SECRET
e THE_DOG_API_X_API_KEY
no sistema ou IDE, ou então editar o arquivo application.properties
nas condfigurações spring.datasource.password
, meu-amigau-api.jwt.secret
e meu-amigau-api.the-dog-api.x-api-key
com suas informações.
Para iniciar o projeto, basta utilizar uma das opções abaixo:
-
Utilizando a IDE Eclipse, basta executar (Run) a classe
MeuAmigauApiApplication
no pacotecom.gft.meuamigau
. -
Utilizando o terminal (PowerShell ou similiar), basta executar na pasta do projeto o comando abaixo:
./mvnw clean package spring-boot:run
Ao executar a aplicação será criado o esquema do banco de dados através do flyway e o banco será automaticamente populado através da classe PopulateDB
, pacote config.populate
, com:
- 5 perfis conforme disponíveis pela classe
RoleName.class
do pacoteenums
:- ROLE_ADMIN
- ROLE_USER
- ROLE_PERSON
- ROLE_VET
- ROLE_CLIENT
- 2 usuários:
- username: admin@email.com
- senha: pass@1234
- perfil(s): ROLE_USUARIO, ROLE_ADMIN
- username: usuario@email.com
- senha: pass@1234
- perfil(s): ROLE_USUARIO
- username: admin@email.com
- 4 pessoas
- 3 clientes
- 2 veterinários
- 1 raça de cachorro (Undefined Race)
- 4 cachorros
- 5 atendimentos
-
Autenticação:
Método URL Perfil(s) Autorizado(s) GET http://localhost:8080/api/v1/auth Público -
Usuários:
Método URL Perfil(s) Autorizado(s) POST http://localhost:8080/api/v1/users ROLE_ADMIN GET http://localhost:8080/api/v1/users ROLE_ADMIN GET http://localhost:8080/api/v1/users/{id} ONLY OWN USER, ROLE_ADMIN PUT http://localhost:8080/api/v1/users/{id} ROLE_ADMIN PATCH http://localhost:8080/api/v1/users/pass/{username} ONLY OWN USER DELETE http://localhost:8080/api/v1/users/{id} ROLE_ADMIN {NOT OWN USER}**_ -
Pessoas:
Método URL Perfil(s) Autorizad(s) POST http://localhost:8080/api/v1/people ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/people ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/people/{id} ROLE_PERSON {ONLY OWN USER}*, ROLE_USER, ROLE_ADMIN PUT http://localhost:8080/api/v1/people/{id} ROLE_ADMIN PATCH http://localhost:8080/api/v1/people/{id}/handle-user/id/{newUserId} ROLE_ADMIN PATCH http://localhost:8080/api/v1/people/{id}/handle-user/username/{username} ROLE_ADMIN DELETE http://localhost:8080/api/v1/people/{id} ROLE_ADMIN -
Clientes:
Método URL Perfil(s) Autorizado(s) POST http://localhost:8080/api/v1/clients ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/clients ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/clients/{id} ROLE_CLIENT {ONLY OWN USER}*, ROLE_USER, ROLE_ADMIN PUT http://localhost:8080/api/v1/clients/{id} ROLE_USER, ROLE_ADMIN DELETE http://localhost:8080/api/v1/clients/{id} ROLE_ADMIN -
Veterinários:
Método URL Perfil(s) Autorizado(s) POST http://localhost:8080/api/v1/vets ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/vets ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/vets/{id} ROLE_VET {ONLY OWN USER}*, ROLE_USER, ROLE_ADMIN PUT http://localhost:8080/api/v1/vets/{id} ROLE_USER, ROLE_ADMIN DELETE http://localhost:8080/api/v1/vets/{id} ROLE_ADMIN -
Raças de Cachorros:
Método URL Perfil(s) Autorizado(s) GET http://localhost:8080/api/v1/breeds ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/breeds/name/{name} ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/breeds/{id} ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/breeds/images/{id} ROLE_VET, ROLE_USER, ROLE_ADMIN -
Cachorros:
Método URL Perfil(s) Autorizado(s) POST http://localhost:8080/api/v1/dogs ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/dogs ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/dogs/clients/{id} ROLE_CLIENT {ONLY OWN USER}*, ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/dogs/{id} ROLE_VET, ROLE_USER, ROLE_ADMIN PUT http://localhost:8080/api/v1/dogs/{id} ROLE_VET, ROLE_USER, ROLE_ADMIN DELETE http://localhost:8080/api/v1/dogs/{id} ROLE_ADMIN -
Atendimentos:
Método URL Perfil(s) Autorizado(s) POST http://localhost:8080/api/v1/attendances ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/attendances ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/attendances/clients/{id} ROLE_CLIENT {ONLY OWN USER}*, ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/attendances/dogs/{id} ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/attendances/vets/{id} ROLE_VET, ROLE_USER, ROLE_ADMIN GET http://localhost:8080/api/v1/attendances/{id} ROLE_VET, ROLE_USER, ROLE_ADMIN PUT http://localhost:8080/api/v1/attendances/{id} ROLE_VET, ROLE_USER, ROLE_ADMIN DELETE http://localhost:8080/api/v1/attendances/{id} ROLE_ADMIN
IMPORTANTE: Como é uma aplicação que possui checagem de permissão de acesso, é necessário que se utilize token para as requisições através do Endpoint, o(s) perfil(s) de acesso(s) estão relacionados nas tabelas acima.
OBSERVAÇÃO: Os perfis marcados com {ONLY OWN USER}*, indica que a permissão é atribuída somente para que o usuário do perfil possa acessar apenas o recurso que tenha relação direta para si.
OBSERVAÇÃO 2: No endpoint de delete o ROLE_ADMIN está marcado com {NOT OWN USER}**, indicando que não é possível executar o processo sendo o próprio usuário do recurso, no caso, um usuário mesmo sendo administrador, não pode se deletar.
OBSERVAÇÃO 3: No endpoint que permite ao próprio usuário trocar a senha a permissão está marcada com: ONLY OWN USER
A documentação completa dos Endpoints estará disponível através do Swagger que estará acessível pelo link quando o projeto iniciar: http://localhost:8080/swagger-ui.html
Também existe arquivo de Coleção do Postman, basta importar o arquivo MeuAmigAU-API.postman_collection.json
que está na pasta docs.
Para fazer a autenticação e utilizar o token no Swagger siga os passos:
1- Na página do Swagger (http://localhost:8080/swagger-ui.html) procure por "1. Autenticação", depois o endpoint POST em "/api/v1/auth Generate Token" e clique em "Try it out"
2- Irá ter um campo de texto (textarea) com um JSON de modelo, altere o email e password para email e senha de usuário já cadastrado (exemplo: admin@email.com), depois é só clicar em "Execute"
3- Após a execução irá aparecer a resposta abaixo na seção "Server response", o token estará no "Response body", copiar somente o token sem as aspas. Se retornar erro 401, o usuário ou senha estão inválidos.
4- Para realizar a autorização de acesso aos endpoist basta clicar no botão "Authorize" ou no ícone de cadeado que aparece em frente aos endpoints que exigem autorização
5- Na janela que abrir, informar o token gerado e copiado no passo 3 e clique em "Authorize". A autorização disponível já está configurada para aceitar Bearer token, portanto é só colar o token sem acrescentar nada
6- Após informar o token, a janela irá mudar com a mensagem de "Authorized" e os ícones de cadeados que aparecem no botão "Authorize" e nos endpoints que exigem autorização irão aparecer como cadeado fechado
Para fazer a autenticação e utilizar o token no Postman siga os passos:
1- No aplicativo Postman é necessário fazer a requisição com método POST na URL http://localhost:8080/api/v1/auth com o nome de usuário e senha no body da requisição. A configuração da requisição está na Coleção "MeuAmigAU! API / Autenticação / Gerar Token de Acesso"
2- A requisição bem sucedida irá retornar resposta 200 OK e o token, copie o token
3- Após o token gerado e copiado, é só ir em qualquer requisição, ir na aba Authorization, escolher a opção "Bearer Token" e colar o token.
Aqui estão listadas sugestões para futuras atualizações:
- Criar campos para guardar timestamps de criação e atualização com o usuário que realizou a operação;
- CRUD para raças de cachorros (Breeds);
- Transformar o delete definitivo em um delete lógico, quando a informação continua presente nos dados, mas fica "inativa" no sistema;
- Para o processo acima funcionar corretamente, deve-se considerar como serão tratados novos cadastros que por ventura violem a restrição de ser único para a entidade/tabela, como email ou documento;
- Criar endpoint no cachorro para poder permitir a troca de cliente;
- Criar endpoint para permitir pesquisar atendimentos por data e hora;
- Para o caso de permitir troca de cliente, criar DTO para atualização de atendimento deixando o campo de cliente disponível para preenchimento, atualmente o cliente é preenchido automaticamente pelo sistema;
- Criar testes unitários com contexto de segurança para testar a acessibilidade dos perfils, assim pode cobrir 100% o
AttendanceService
; - Criar testes que cubram 100% o
BreedService
em relação à conexão da API externa e sem a necessidade da resposta real. Verificar a implementação de Mock Server para não necessitar de conectar ao recurso real.
Dúvidas, problemas ou sugestões: abrir Issue ou Merge Request.
Desafio proposto por Michel, Ubiratran, Clécio e equipe do Programa Start da GFT.
Feito por Luis Carlos Zancanela
O código deve ser elegante para agradar a quem olhar.
Done in 29/07/2022