API REST em .NET 8 para execução segura de scripts JavaScript de pré-processamento de dados. Sistema robusto e funcional para transformação de dados financeiros.
O sistema segue uma arquitetura em camadas com separação clara de responsabilidades:
- Datarisk.Api: Controllers REST com tratamento de erro
- Datarisk.Dominio: Entidades e interfaces do negócio
- Datarisk.Infra: Persistência com EF Core e PostgreSQL
- Datarisk.Servicos: Validação e execução de scripts
- Datarisk.Processamento.Funcional: Demonstração com F# funcional
- Datarisk.Tests: Testes unitários
- .NET 8: Framework principal
- ASP.NET Core: API REST
- PostgreSQL: Banco de dados
- Entity Framework Core: ORM
- Jint: Engine JavaScript segura
- F#: Programação funcional
- Docker: Containerização
- xUnit: Testes unitários
- Swagger: Documentação da API
- Docker e Docker Compose
- .NET 8 SDK (para desenvolvimento local)
# Clonar o repositório
git clone <repository-url>
cd Datarisk
# Executar com docker-compose
docker-compose up --build
# A API estará disponível em:
# - http://localhost:8080
# - Swagger: http://localhost:8080/docs# Restaurar dependências
dotnet restore
# Executar PostgreSQL via Docker
docker run --name postgres-datarisk -e POSTGRES_DB=datarisk -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:16-alpine
# Executar migrations
dotnet ef database update --project Datarisk.Infra --startup-project Datarisk.Api
# Executar a aplicação
dotnet run --project Datarisk.Api
# Executar testes
dotnet testCria um novo script JavaScript.
curl -X POST http://localhost:8080/api/scripts \
-H "Content-Type: application/json" \
-d '{
"nome": "ProcessadorEmpresarial",
"conteudoJs": "function process(data) { const corporativeData = data.filter(item => item.produto === \"Empresarial\"); const byQuarterAndIssuer = corporativeData.reduce((acc, item) => { const key = \`\${item.trimestre}-\${item.nomeBandeira}\`; if (!acc[key]) { acc[key] = { trimestre: item.trimestre, nomeBandeira: item.nomeBandeira, qtdCartoesEmitidos: 0, qtdCartoesAtivos: 0, qtdTransacoesNacionais: 0, valorTransacoesNacionais: 0 }; } acc[key].qtdCartoesEmitidos += item.qtdCartoesEmitidos; acc[key].qtdCartoesAtivos += item.qtdCartoesAtivos; acc[key].qtdTransacoesNacionais += item.qtdTransacoesNacionais; acc[key].valorTransacoesNacionais += item.valorTransacoesNacionais; return acc; }, {}); return Object.values(byQuarterAndIssuer); }"
}'Lista todos os scripts cadastrados.
curl http://localhost:8080/api/scriptsObtém um script específico por ID.
curl http://localhost:8080/api/scripts/{script-id}Executa um script com dados específicos.
curl -X POST http://localhost:8080/api/execucoes \
-H "Content-Type: application/json" \
-d '{
"idScript": "{script-id}",
"dados": [
{
"produto": "Empresarial",
"trimestre": "2024Q1",
"nomeBandeira": "Visa",
"qtdCartoesEmitidos": 1000,
"qtdCartoesAtivos": 800,
"qtdTransacoesNacionais": 5000,
"valorTransacoesNacionais": 100000.50
},
{
"produto": "Empresarial",
"trimestre": "2024Q1",
"nomeBandeira": "Mastercard",
"qtdCartoesEmitidos": 750,
"qtdCartoesAtivos": 600,
"qtdTransacoesNacionais": 3500,
"valorTransacoesNacionais": 75000.25
}
]
}'Consulta status e resultado de uma execução.
curl http://localhost:8080/api/execucoes/{execucao-id}curl http://localhost:8080/api/health/healthzcurl http://localhost:8080/api/health/readinessO script deve expor uma função process que recebe dados JSON e retorna o resultado processado:
function process(data) {
// Filtrar apenas dados empresariais
const corporativeData = data.filter(item => item.produto === "Empresarial");
// Agrupar por trimestre e bandeira
const byQuarterAndIssuer = corporativeData.reduce((acc, item) => {
const key = `${item.trimestre}-${item.nomeBandeira}`;
if (!acc[key]) {
acc[key] = {
trimestre: item.trimestre,
nomeBandeira: item.nomeBandeira,
qtdCartoesEmitidos: 0,
qtdCartoesAtivos: 0,
qtdTransacoesNacionais: 0,
valorTransacoesNacionais: 0
};
}
acc[key].qtdCartoesEmitidos += item.qtdCartoesEmitidos;
acc[key].qtdCartoesAtivos += item.qtdCartoesAtivos;
acc[key].qtdTransacoesNacionais += item.qtdTransacoesNacionais;
acc[key].valorTransacoesNacionais += item.valorTransacoesNacionais;
return acc;
}, {});
return Object.values(byQuarterAndIssuer);
}- Criar Script: Use POST /api/scripts para cadastrar um script JavaScript
- Verificar Script: Use GET /api/scripts/{id} para confirmar que foi criado
- Executar Script: Use POST /api/execucoes com dados de entrada
- Monitorar Execução: Use GET /api/execucoes/{id} para acompanhar o progresso
- Obter Resultado: Quando status for "Sucesso", o campo resultadoJson terá o resultado
- Scripts executam em sandbox Jint sem acesso ao sistema operacional
- Validação de padrões perigosos (require, import, eval, etc.)
- Limite de tempo de execução (5 segundos)
- Limite de tamanho do script (100KB)
- Limite de payload de dados (5MB)
- Timeout e tratamento de exceções
id(UUID): Identificador úniconome(varchar): Nome do scriptconteudo_js(text): Código JavaScriptcriado_em(timestamp): Data de criaçãovalido(bool): Se passou na validaçãohash_conteudo(varchar): Hash do conteúdo
id(UUID): Identificador únicoscript_id(UUID): Referência ao scriptstatus(enum): PENDENTE|EM_EXECUCAO|SUCESSO|FALHAresultado_json(jsonb): Resultado da execuçãoerro(text): Mensagem de erro se houvercriado_em(timestamp): Data de criaçãoiniciado_em(timestamp): Início da execuçãofinalizado_em(timestamp): Fim da execuçãotempo_execucao_ms(int): Tempo em milissegundos
- Logs estruturados com correlação por request ID
- Métricas de tempo de execução
- Health checks em /api/health/healthz e /api/health/readiness
- Documentação automática em /docs
# Verificar logs
docker-compose logs api
# Verificar se PostgreSQL está rodando
docker-compose logs postgres# Verificar se o banco está acessível
docker-compose exec postgres pg_isready -U postgres
# Recriar containers
docker-compose down -v
docker-compose up --build- Verifique se contém função
process - Verifique se não usa padrões proibidos
- Consulte logs da aplicação para detalhes
- Adicione entidades no projeto Dominio
- Implemente repositórios na Infra
- Crie serviços na camada Servicos
- Exponha via controllers na Api
- Adicione testes unitários
dotnet test --logger trx --results-directory ./TestResults# Adicionar nova migration
dotnet ef migrations add NomeDaMigration --project Datarisk.Infra --startup-project Datarisk.Api
# Aplicar migrations
dotnet ef database update --project Datarisk.Infra --startup-project Datarisk.ApiProgram.cs: Configuração da aplicação e DIDatariskDbContext.cs: Contexto do Entity FrameworkExecutorScript.cs: Execução segura de JavaScriptProcessadorExecucao.cs: Serviço de execuçãodocker-compose.yml: Orquestração dos containersLibrary.fs: Implementação funcional F# equivalente
Datarisk - Sistema robusto e seguro para processamento de dados financeiros com JavaScript.