469 lines
21 KiB
Python
469 lines
21 KiB
Python
from sqlalchemy import inspect
|
|
from app.database.models import HistoricoAlteracoes, HistoricoDelete, HistoricoUpdate
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
# def audit_log(func):
|
|
# async def wrapper(*args, **kwargs):
|
|
# self = args[0] # O primeiro argumento será 'self', que é a instância do repositório
|
|
# session = self.session
|
|
#
|
|
# # Verificar se o modelo está habilitado para auditoria
|
|
# if not getattr(self.model, 'log_auditoria_habilitado', False):
|
|
# # Executa a função original sem auditoria
|
|
# result = await func(*args, **kwargs)
|
|
#
|
|
# # Realiza o commit e refresh
|
|
# await session.commit()
|
|
#
|
|
# # Verificar se é uma operação de UPDATE ou DELETE
|
|
# db_models = result.get("db_models")
|
|
# operation = result.get("operation")
|
|
# rowcount = result.get("rowcount")
|
|
#
|
|
# # Atualiza o db_model com refresh em caso de UPDATE
|
|
# if operation == "UPDATE" and db_models:
|
|
# for db_model in db_models:
|
|
# await session.refresh(db_model)
|
|
#
|
|
# # Retorna o que a rota espera: o db_model para update ou rowcount para delete
|
|
# if operation == "UPDATE":
|
|
# if len(db_models) == 1:
|
|
# return db_models[0].__dict__ # Retorna o único modelo como dicionário
|
|
# else:
|
|
# return db_models # Retorna a lista de modelos
|
|
# else:
|
|
# return rowcount # Retorna rowcount no caso de DELETE
|
|
#
|
|
# # Caso a auditoria esteja habilitada, continuar com o processo de auditoria
|
|
# # Tenta executar a função original e captura o retorno
|
|
# result = await func(*args, **kwargs)
|
|
#
|
|
# # Captura o db_model e a operação realizada
|
|
# db_models = result.get("db_models")
|
|
# original_models = result.get("original_models")
|
|
# operation = result.get("operation")
|
|
# rowcount = result.get("rowcount")
|
|
#
|
|
# # Para operações de UPDATE
|
|
# log_entries = []
|
|
# if operation == "UPDATE" and db_models:
|
|
# timestamp = datetime.now(timezone.utc).replace(tzinfo=None)
|
|
#
|
|
# # Iterar pelos modelos atualizados
|
|
# for db_model, original_model in zip(db_models, original_models):
|
|
# inspector = inspect(db_model)
|
|
# primary_key = str(inspect(db_model).identity[0])
|
|
#
|
|
# # Verificar alterações em cada atributo
|
|
# for attr in inspector.attrs:
|
|
# old_value = original_model.get(attr.key, None)
|
|
# new_value = getattr(db_model, attr.key, None)
|
|
#
|
|
# # Confirmar se houve mudança nos valores
|
|
# if old_value != new_value:
|
|
# log_entry = Historico_Alteracoes(
|
|
# tabela=db_model.__tablename__,
|
|
# coluna=attr.key,
|
|
# valor_antigo=str(old_value) if old_value else None,
|
|
# valor_novo=str(new_value) if new_value else None,
|
|
# data_modificacao=timestamp,
|
|
# action='UPDATE',
|
|
# usuario_id=kwargs.get('user_id'),
|
|
# registro_id=primary_key
|
|
# )
|
|
# log_entries.append(log_entry)
|
|
#
|
|
# # Verificar se é uma operação de DELETE
|
|
# elif operation == "DELETE":
|
|
# timestamp_naive = datetime.now(timezone.utc).replace(tzinfo=None)
|
|
#
|
|
# if isinstance(db_models, list): # Caso seja uma lista de modelos deletados (delete_many)
|
|
# for db_model in db_models:
|
|
# primary_key = str(inspect(db_model).identity[0])
|
|
# deletado = {attr: value for attr, value in db_model.__dict__.items()
|
|
# if not isinstance(value, list) and not attr.startswith('_')}
|
|
#
|
|
# json_deletado = json.dumps(deletado, default=str)
|
|
#
|
|
# log_entry = Historico_Alteracoes(
|
|
# tabela=db_model.__tablename__,
|
|
# coluna='N/A', # Especificar que esta é uma operação de DELETE
|
|
# valor_antigo=str(deletado), # Salvar o estado completo do modelo deletado
|
|
# valor_novo=None, # Valor novo é None porque o registro foi deletado
|
|
# data_modificacao=timestamp_naive,
|
|
# action='DELETE',
|
|
# usuario_id=kwargs.get('user_id'),
|
|
# registro_id=primary_key
|
|
# )
|
|
# log_entries.append(log_entry)
|
|
#
|
|
# else: # Caso seja apenas um modelo (delete_one)
|
|
# primary_key = str(inspect(db_models).identity[0])
|
|
# deletado = {attr: value for attr, value in db_models.__dict__.items()
|
|
# if not isinstance(value, list) and not attr.startswith('_')}
|
|
#
|
|
# log_entry = Historico_Alteracoes(
|
|
# tabela=db_models.__tablename__,
|
|
# coluna='N/A', # Especificar que esta é uma operação de DELETE
|
|
# valor_antigo=str(deletado), # Salvar o estado completo do modelo deletado
|
|
# valor_novo=None, # Valor novo é None porque o registro foi deletado
|
|
# data_modificacao=timestamp_naive,
|
|
# action='DELETE',
|
|
# usuario_id=kwargs.get('user_id'),
|
|
# registro_id=primary_key
|
|
# )
|
|
# log_entries.append(log_entry)
|
|
#
|
|
# # Se houver log_entries a serem salvas (no caso de UPDATE ou DELETE)
|
|
# if log_entries:
|
|
# session.add_all(log_entries)
|
|
#
|
|
# # Realizar o commit no final, para ambos UPDATE e DELETE
|
|
# await session.commit()
|
|
#
|
|
# # Se for um update, atualiza o db_model com refresh
|
|
# if operation == "UPDATE" and db_models:
|
|
# for db_model in db_models:
|
|
# await session.refresh(db_model)
|
|
#
|
|
# # Retorna o que a rota espera: o db_model para update ou rowcount para delete
|
|
# if operation == "UPDATE":
|
|
# if len(db_models) == 1:
|
|
# return db_models[0].__dict__ # Retorna o único modelo como dicionário
|
|
# else:
|
|
# return db_models # Retorna a lista de modelos
|
|
# else:
|
|
# return rowcount # Retorna rowcount no caso de DELETE
|
|
#
|
|
# return wrapper
|
|
|
|
# def audit_log(func):
|
|
# async def wrapper(*args, **kwargs):
|
|
# self = args[0] # O primeiro argumento será 'self', que é a instância do repositório
|
|
# session = self.session
|
|
#
|
|
# # Verificar se o modelo está habilitado para auditoria
|
|
# if not getattr(self.model, 'log_auditoria_habilitado', False):
|
|
# # Executa a função original sem auditoria
|
|
# result = await func(*args, **kwargs)
|
|
#
|
|
# # Realiza o commit e refresh
|
|
# await session.commit()
|
|
#
|
|
# # Verificar se é uma operação de UPDATE ou DELETE
|
|
# db_models = result.get("db_models")
|
|
# operation = result.get("operation")
|
|
# rowcount = result.get("rowcount")
|
|
#
|
|
# # Atualiza o db_model com refresh em caso de UPDATE
|
|
# if operation == "UPDATE" and db_models:
|
|
# for db_model in db_models:
|
|
# await session.refresh(db_model)
|
|
# print("refreshing relaizado")
|
|
#
|
|
# # Retorna o que a rota espera: o db_model para update ou rowcount para delete
|
|
# if operation == "UPDATE":
|
|
# if len(db_models) == 1:
|
|
# print(db_models[0].__dict__)
|
|
# return db_models[0].__dict__ # Retorna o único modelo como dicionário
|
|
# else:
|
|
# return db_models # Retorna a lista de modelos
|
|
# else:
|
|
# return rowcount # Retorna rowcount no caso de DELETE
|
|
#
|
|
# # Caso a auditoria esteja habilitada, continuar com o processo de auditoria
|
|
# # Tenta executar a função original e captura o retorno
|
|
# result = await func(*args, **kwargs)
|
|
#
|
|
# # Captura o db_model e a operação realizada
|
|
# db_models = result.get("db_models")
|
|
# original_models = result.get("original_models")
|
|
# operation = result.get("operation")
|
|
# rowcount = result.get("rowcount")
|
|
#
|
|
# # Para operações de UPDATE
|
|
# log_entries = []
|
|
# timestamp = datetime.now(timezone.utc).replace(tzinfo=None)
|
|
# if operation == "UPDATE" and db_models:
|
|
# # timestamp = datetime.now(timezone.utc).replace(tzinfo=None)
|
|
#
|
|
# # Iterar pelos modelos atualizados
|
|
# for db_model, original_model in zip(db_models, original_models):
|
|
# inspector = inspect(db_model)
|
|
# primary_key = str(inspect(db_model).identity[0])
|
|
#
|
|
# # Registro na tabela HistoricoAlteracoes
|
|
# log_entry_update = HistoricoAlteracoes(
|
|
# tabela=db_model.__tablename__,
|
|
# data_modificacao=timestamp,
|
|
# action='UPDATE',
|
|
# usuario_id=kwargs.get('user_id'),
|
|
# registro_id=primary_key
|
|
# )
|
|
# session.add(log_entry_update)
|
|
#
|
|
# # Verificar alterações em cada atributo
|
|
# for attr in inspector.attrs:
|
|
# old_value = original_model.get(attr.key, None)
|
|
# new_value = getattr(db_model, attr.key, None)
|
|
#
|
|
# # Confirmar se houve mudança nos valores
|
|
# if old_value != new_value:
|
|
# log_update = HistoricoUpdate(
|
|
# coluna=attr.key,
|
|
# valor_antigo=str(old_value) if old_value else None,
|
|
# valor_novo=str(new_value) if new_value else None,
|
|
# alteracao=log_entry_update # Relacionando ao log de alteração principal
|
|
# )
|
|
# session.add(log_update)
|
|
#
|
|
# # Verificar se é uma operação de DELETE
|
|
# elif operation == "DELETE":
|
|
# _timestamp_naive = datetime.now(timezone.utc).replace(tzinfo=None)
|
|
#
|
|
# if isinstance(db_models, list): # Caso seja uma lista de modelos deletados (delete_many)
|
|
# for db_model in db_models:
|
|
# primary_key = str(inspect(db_model).identity[0])
|
|
#
|
|
# # Filtra os campos que não são relationship
|
|
#
|
|
# def is_relationship(attr_name, model):
|
|
# """
|
|
# Função que verifica se um atributo é do tipo relacionamento no SQLAlchemy
|
|
# """
|
|
# # Inspeciona o modelo SQLAlchemy
|
|
# mapper = inspect(model.__class__)
|
|
#
|
|
# # Acessa todas as relationships do modelo
|
|
# relationships = mapper.relationships
|
|
#
|
|
# # Verifica se o atributo atual é uma relationship
|
|
# return attr_name in relationships
|
|
#
|
|
# deletado = {attr: value for attr, value in db_model.__dict__.items()
|
|
# if
|
|
# not isinstance(value, list) and not attr.startswith('_') and not
|
|
# is_relationship(attr, db_model)}
|
|
#
|
|
# # Registro na tabela HistoricoAlteracoes
|
|
# log_entry_delete = HistoricoAlteracoes(
|
|
# tabela=db_model.__tablename__,
|
|
# data_modificacao=timestamp,
|
|
# action='DELETE',
|
|
# usuario_id=kwargs.get('user_id'),
|
|
# registro_id=primary_key
|
|
# )
|
|
# session.add(log_entry_delete)
|
|
#
|
|
# log_delete = HistoricoDelete(
|
|
# registro_deletado=str(deletado), # Serializar o registro deletado
|
|
# alteracao=log_entry_delete # Relacionando ao log de alteração principal
|
|
# )
|
|
# session.add(log_delete)
|
|
#
|
|
# else: # Caso seja apenas um modelo (delete_one)
|
|
# primary_key = str(inspect(db_models).identity[0])
|
|
#
|
|
# def is_relationship(attr_name, model):
|
|
# """
|
|
# Função que verifica se um atributo é do tipo relacionamento no SQLAlchemy
|
|
# """
|
|
# # Inspeciona o modelo SQLAlchemy
|
|
# mapper = inspect(model.__class__)
|
|
#
|
|
# # Acessa todas as relationships do modelo
|
|
# relationships = mapper.relationships
|
|
#
|
|
# # Verifica se o atributo atual é uma relationship
|
|
# return attr_name in relationships
|
|
#
|
|
# # Filtra os campos que não são relationship
|
|
# deletado = {attr: value for attr, value in db_models.__dict__.items()
|
|
# if not isinstance(value, list) and not attr.startswith('_') and not
|
|
# is_relationship(attr, db_models)}
|
|
#
|
|
# # Registro na tabela HistoricoAlteracoes
|
|
# log_entry_delete = HistoricoAlteracoes(
|
|
# tabela=db_models.__tablename__,
|
|
# data_modificacao=timestamp,
|
|
# action='DELETE',
|
|
# usuario_id=kwargs.get('user_id'),
|
|
# registro_id=primary_key
|
|
# )
|
|
# session.add(log_entry_delete)
|
|
#
|
|
# log_delete = HistoricoDelete(
|
|
# registro_deletado=str(deletado), # Serializar o registro deletado
|
|
# alteracao=log_entry_delete # Relacionando ao log de alteração principal
|
|
# )
|
|
# session.add(log_delete)
|
|
#
|
|
# # Se houver log_entries a serem salvas (no caso de UPDATE ou DELETE)
|
|
# if log_entries:
|
|
# session.add_all(log_entries)
|
|
#
|
|
# # Realizar o commit no final, para ambos UPDATE e DELETE
|
|
# await session.commit()
|
|
#
|
|
# # Se for um update, atualiza o db_model com refresh
|
|
# if operation == "UPDATE" and db_models:
|
|
# for db_model in db_models:
|
|
# await session.refresh(db_model)
|
|
#
|
|
# # Retorna o que a rota espera: o db_model para update ou rowcount para delete
|
|
# if operation == "UPDATE":
|
|
# if len(db_models) == 1:
|
|
# return db_models[0].__dict__ # Retorna o único modelo como dicionário
|
|
# else:
|
|
# return db_models # Retorna a lista de modelos
|
|
# else:
|
|
# return rowcount # Retorna rowcount no caso de DELETE
|
|
#
|
|
# return wrapper
|
|
|
|
def audit_log(func):
|
|
async def wrapper(*args, **kwargs):
|
|
self = args[0] # 'self' é a instância do repositório
|
|
session = self.session
|
|
|
|
# Se auditoria não estiver habilitada, processa sem auditoria
|
|
if not getattr(self.model, 'log_auditoria_habilitado', False):
|
|
result = await func(*args, **kwargs)
|
|
await session.commit()
|
|
|
|
db_models = result.get("db_models")
|
|
operation = result.get("operation")
|
|
rowcount = result.get("rowcount")
|
|
|
|
if operation == "UPDATE" and db_models:
|
|
if isinstance(db_models, list):
|
|
for db_model in db_models:
|
|
await session.refresh(db_model)
|
|
print("refreshing realizado")
|
|
else:
|
|
await session.refresh(db_models)
|
|
# Retorna exatamente o que a função original produziu
|
|
if operation == "UPDATE":
|
|
return db_models
|
|
else:
|
|
return rowcount
|
|
|
|
# Auditoria habilitada: chama a função original e captura o retorno
|
|
result = await func(*args, **kwargs)
|
|
db_models = result.get("db_models")
|
|
original_models = result.get("original_models")
|
|
operation = result.get("operation")
|
|
rowcount = result.get("rowcount")
|
|
|
|
# Variável de controle: se o retorno for um objeto único, single_update será True.
|
|
single_update = False
|
|
original_db_model = None
|
|
if not isinstance(db_models, list):
|
|
single_update = True
|
|
original_db_model = db_models # Guarda o objeto único original
|
|
db_models = [db_models] # Encapsula para processamento da auditoria
|
|
original_models = [original_models]
|
|
|
|
# Processamento da auditoria para UPDATE
|
|
if operation == "UPDATE" and db_models:
|
|
timestamp = datetime.now(timezone.utc).replace(tzinfo=None)
|
|
for db_model, original_model in zip(db_models, original_models):
|
|
inspector = inspect(db_model)
|
|
primary_key = str(inspect(db_model).identity[0])
|
|
log_entry_update = HistoricoAlteracoes(
|
|
tabela=db_model.__tablename__,
|
|
data_modificacao=timestamp,
|
|
action='UPDATE',
|
|
usuario_id=kwargs.get('user_id'),
|
|
registro_id=primary_key
|
|
)
|
|
session.add(log_entry_update)
|
|
|
|
# Itera pelos atributos mapeados e registra alterações
|
|
for attr in inspector.attrs:
|
|
old_value = original_model.get(attr.key, None)
|
|
new_value = getattr(db_model, attr.key, None)
|
|
if old_value != new_value:
|
|
log_update = HistoricoUpdate(
|
|
coluna=attr.key,
|
|
valor_antigo=str(old_value) if old_value is not None else None,
|
|
valor_novo=str(new_value) if new_value is not None else None,
|
|
alteracao=log_entry_update
|
|
)
|
|
session.add(log_update)
|
|
|
|
# Processamento da auditoria para DELETE
|
|
elif operation == "DELETE":
|
|
_timestamp_naive = datetime.now(timezone.utc).replace(tzinfo=None)
|
|
if isinstance(db_models, list): # Caso seja delete_many
|
|
for db_model in db_models:
|
|
primary_key = str(inspect(db_model).identity[0])
|
|
|
|
def is_relationship(attr_name, model):
|
|
mapper = inspect(model.__class__)
|
|
return attr_name in mapper.relationships
|
|
|
|
deletado = {
|
|
attr: value
|
|
for attr, value in db_model.__dict__.items()
|
|
if
|
|
not isinstance(value, list) and not attr.startswith('_') and not is_relationship(attr, db_model)
|
|
}
|
|
log_entry_delete = HistoricoAlteracoes(
|
|
tabela=db_model.__tablename__,
|
|
data_modificacao=_timestamp_naive,
|
|
action='DELETE',
|
|
usuario_id=kwargs.get('user_id'),
|
|
registro_id=primary_key
|
|
)
|
|
session.add(log_entry_delete)
|
|
log_delete = HistoricoDelete(
|
|
registro_deletado=str(deletado),
|
|
alteracao=log_entry_delete
|
|
)
|
|
session.add(log_delete)
|
|
else: # Caso delete_one
|
|
primary_key = str(inspect(db_models).identity[0])
|
|
|
|
def is_relationship(attr_name, model):
|
|
mapper = inspect(model.__class__)
|
|
return attr_name in mapper.relationships
|
|
|
|
deletado = {
|
|
attr: value
|
|
for attr, value in db_models.__dict__.items()
|
|
if not isinstance(value, list) and not attr.startswith('_') and not is_relationship(attr, db_models)
|
|
}
|
|
log_entry_delete = HistoricoAlteracoes(
|
|
tabela=db_models.__tablename__,
|
|
data_modificacao=_timestamp_naive,
|
|
action='DELETE',
|
|
usuario_id=kwargs.get('user_id'),
|
|
registro_id=primary_key
|
|
)
|
|
session.add(log_entry_delete)
|
|
log_delete = HistoricoDelete(
|
|
registro_deletado=str(deletado),
|
|
alteracao=log_entry_delete
|
|
)
|
|
session.add(log_delete)
|
|
|
|
# Realiza o commit final após registrar a auditoria
|
|
await session.commit()
|
|
|
|
# Para operações de UPDATE, faz refresh dos objetos
|
|
if operation == "UPDATE" and db_models:
|
|
for db_model in db_models:
|
|
await session.refresh(db_model)
|
|
|
|
# Retorno final: se for update e se for update_by_id (single_update=True),
|
|
# retorna o objeto único; caso contrário, retorna a lista, mantendo o contrato original.
|
|
if operation == "UPDATE":
|
|
return original_db_model if single_update else db_models
|
|
else:
|
|
return rowcount
|
|
|
|
return wrapper
|