import os import tempfile from abc import ABC, abstractmethod from typing import Any from fastapi import HTTPException from pydantic import BaseModel class HolmesCliente(BaseModel): """Credenciais do Holmes. Cada cliente tem a propria conta de integracao.""" token_api: str usuario: str senha: str class ClienteConfig(BaseModel): """ Os DADOS do cliente. Fica separado da classe de proposito: dado da pra carregar do banco, classe nao. No dia que cliente novo deixar de exigir deploy, e daqui que a config vai sair — o resto do codigo nao muda. """ nome: str razao_social: str cnpj: str holmes: HolmesCliente class ClienteBase(ABC): """ Base de todo cliente atendido pela API. Regra de ouro: aqui so entra o que muda de COMPORTAMENTO entre clientes. Valor (CNPJ, token, id de fluxo) vai em `config`, nao em atributo de classe solto — senao a heranca vira dicionario com passos extras. A subclasse satisfaz `config` e `modulos` como atributo de classe mesmo, nao precisa escrever @property. """ @property @abstractmethod def config(self) -> ClienteConfig: ... @property @abstractmethod def modulos(self) -> dict[str, Any]: """Processos habilitados: {"compras": ScarduaCompras(), ...}""" # --- comum a todos: ninguem sobrescreve --- @property def nome(self) -> str: return self.config.nome def modulo(self, nome: str) -> Any: """Pega um processo habilitado do cliente, ou 404 se ele nao tem.""" if modulo := self.modulos.get(nome): return modulo raise HTTPException( status_code=404, detail=f"modulo '{nome}' nao habilitado para {self.nome}", ) @property def cache_token_holmes(self) -> str: """ Arquivo de cache do token do Holmes, um por cliente. Sem isso dois clientes com contas diferentes sobrescrevem o token um do outro (ver _TOKEN_FILE em app/services/holmes.py). """ return os.path.join( tempfile.gettempdir(), f"holmes_token_{self.nome}.json" ) def __repr__(self) -> str: return f""