Casos de Uso
É a camada responsável pelas regras de negócio, como por exemplo criar um usuário. Nossa camada de tratamento de dados, atualmente isolamos nossas regras de negócio em outro arquivo, sendo o caso de uso mais um gerenciador das camadas de tratamento de dados, banco de dados e regras de negócio, funcionando como uma “cola”.
Ao recebermos a requisição pela API, ela é repassada ao UseCase, por dentro de um Decorator, que utiliza um objeto com os campos dos dados a serem validados, para garantir que a requisição está ok.
Antes de ser feito o UseCase, é preciso definir os objetos de Entidades no arquivo entities.py
, e os objetos de request no arquivo requests.py
dentro do recurso.
Entities
As entities são objetos Python que devem presentar os dados da requisição:
from hadron import Model, types, validators
class User(Model):
id = types.Int()
fullname = types.String(
validators=[validators.length(min=3, max=64)], required=False
)
username = types.String(
validators=[validators.length(min=3, max=30)], required=False
)
email = types.String(validators=[validators.length(max=254)])
password_hash = types.String()
superuser = types.Boolean(required=False, default=False)
has_console = types.Boolean(required=False, default=False)
allow_metrics = types.Boolean(required=False, default=False)
level = types.String(
validators=[validators.enum(options=["n1", "n2"])], required=False
)
roles = types.List()
created_at = types.DateTime()
updated_at = types.DateTime()
password = types.String(
validators=[validators.length(min=5, max=30)], required=False
)
require_password_to_change = types.Boolean(default=False)
Realizamos todas as validacões que não se referem a regra de negócio e sim aos dados na entidade, você pode ler mais sobre nosso validador Hadron.
Requests
Nos objetos de request, herdamos as entidades e através do class Meta
, podemos incluir ou excluir campos do objeto herdado.
from hadron import Model, types, validators
from itflex_auth.entities import User
DEFAULT_PAGE_SIZE = 15
class CreateUser(User):
consumer = types.Any()
roles = types.List(types.Int)
wizard = types.Boolean(required=False, default=False)
class Meta:
include = [
"username",
"fullname",
"email",
"superuser",
"password",
"has_console",
"level",
]
class GetUserById(Model):
id = types.Int()
class UpdateUser(User):
consumer = types.Any()
roles = types.List(types.Int)
class Meta:
include = [
"id",
"fullname",
"email",
"superuser",
"password",
"has_console",
"level",
]
class DeleteUser(User):
consumer = types.Any()
class Meta:
include = ["id"]
UseCase
Após passar pelas validações, o UseCase segue um fluxo de operações:
- Realizar as chamadas do BussinesRules para validar regras de negócio
- Realizar a chamada do repo para armezanar os dados
- Realizar a chamada de auditoria para registrar quem criou/alterou/deletou o item
- Publicar um evento de que o item foi criado/alterado/deletado
Para facilitar o trabalho, desenvolvemos uma classe para abstrair alguns métodos. A abstração se encontra no arquivo BaseUC. Segue abaixo um exemplo da implementação do UseCase com a abtração.
from itflex.common.response import ItemResp
from itflex.hadron_request import register_request
from itflex.useuserses import BaseUC
from itflex_audit.action_logs.interfaces import IAuditEvents
from itflex_audit.action_logs.requests import CreateActionLog
from itflex_certs.users.bussiness import UsersBussiness
from itflex_certs.users.events import UsersEvents
from itflex_certs.users.repo import SQLUsersRepo
from itflex_certs.users.requests import CreateUser, UpdateUser
class UsersUC(BaseUC):
def __init__(
self, repo: SQLUsersRepo, events: UsersEvents, audit: IAuditEvents
):
super().__init__(repo, events, audit, user_audit)
@register_request(CreateUser)
def create(self, req: CreateUser) -> ItemResp:
buss = UsersBussiness(uc=self, req=req).check_name().create_user()
if not buss.ok:
return buss.response
user_audit(self.audit, req.consumer, buss.user, "create")
self.events.created(buss.user)
return ItemResp(item=buss.user)
@register_request(UpdateUser)
def update(self, req: UpdateUser) -> ItemResp:
buss = (
UsersBussiness(uc=self, req=req)
.get_user()
.update_user()
.get_user()
)
if not buss.ok:
return buss.response
user_audit(self.audit, req.consumer, buss.user, "update")
self.events.updated(buss.user)
return ItemResp(item=buss.user)
Atenção ao implementar o UseCase:
-
Na maioria dos casos, o UseCase tem um repo que será usado para salvar e consultar os dados. Temos por padrão que não utilizamos repos de outros UseCases de forma direta, vide excessões.
-
No UseCase, podemos utilizar outros UseCases para auxiliar na construção das regras negócio. Mas só é permitido usar os métodos de consulta dados, exemplo:
get_by_id
,get_all
,get_page
.
Auditória das ações do usuário
Como trabalhamos com serviço crítico do cliente, qualquer alteração errada feita pode parar a rede do cliente e gerar inconsistência no produto. Por esse motivo devemos registrar as ações executadas pelo usuário logado no FwFlex.
Exemplos:
- Alterar regras de firewall.
- Reiniciar algum serviço do linux.
- Cadastrar conexão de rede.
Se observar no exemplo do UsersUC
citado acima, esta faltando a função user_audit
. Essa função é responsável por registrar as ações do usuário logado, realizadas no cadastro de usuário. Que neste exemplo será registrado quando:
- Cadastrar um novo usuário.
- Editar um usuário.
- Apagar um usuário.
Segue abaixo exemplo da implementação da auditória.
def user_audit(audit, consumer, user, action):
payload = None
if action != "delete":
payload = user.dump(exclude=("created_at", "updated_at"))
audit.log(
CreateActionLog(
consumer=consumer,
module="auth",
target_type="users",
target_desc=user.username,
target_id=user.id,
action=action,
payload=payload,
)
)