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,
        )
    )