20250516 - V1.1 - Código refatorado para melhor escalabilidade

This commit is contained in:
Giuliano Paschoalino 2025-05-16 17:29:38 -03:00
parent 5e2f7c04d9
commit 4bb348cb7f
19 changed files with 542 additions and 369 deletions

3
.gitignore vendored
View File

@ -361,3 +361,6 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# History for Visual Studio
.history/

1
Mappers/README.md Normal file
View File

@ -0,0 +1 @@
// This folder will contain mapping logic between API/DB and domain models if needed

59
Models/Entities.cs Normal file
View File

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
namespace Pipefy.Models
{
public class RootObject
{
public required Node node { get; set; }
}
public class RootGestor
{
public NodeGestor node { get; set; }
}
public class Data
{
public Node node { get; set; }
}
public class Node
{
public string id { get; set; }
public Record_Fields[] record_fields { get; set; }
}
public class NodeGestor
{
public string? id { get; set; }
public Record_Fields[] record_fields { get; set; }
}
public class Record_Fields
{
public Field field { get; set; }
public string? value { get; set; }
public string[] array_value { get; set; }
}
public class Field
{
public string? id { get; set; }
}
public class ClasseEmpresas
{
public string? c_digo_smart { get; set; }
public string? nome_da_empresa { get; set; }
public string? modalidade { get; set; }
public string? gestores { get; set; }
public string rec_id { get; set; }
}
public class ClasseGestores
{
public string? id { get; set; }
public string? gestores { get; set; }
}
public class AppSettings
{
public string DB_PATH { get; set; }
public string PIPEFY_API_TOKEN { get; set; }
public string PIPEFY_API_URL { get; set; }
public string PIPEFY_TABLE_ID { get; set; }
public string PIPEFY_TABLE_ID_GESTORES { get; set; }
}
}

1
Models/README.md Normal file
View File

@ -0,0 +1 @@
// This folder will contain all data models (move from data.cs)

View File

@ -2,13 +2,19 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>1b2d76eb-7ab5-428f-9f67-13b6bf96375d</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />

View File

@ -1,337 +1,96 @@
using System;
using System.Data.OleDb;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Reflection.Metadata;
using System.Data.OleDb;
using Pipefy;
using Microsoft.VisualBasic;
using System.Net.Quic;
using Pipefy.Models;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Microsoft.Extensions.Configuration.UserSecrets;
using static System.Net.Mime.MediaTypeNames;
using System.Text;
using System.Timers;
using Pipefy.Services;
using Microsoft.Extensions.DependencyInjection;
class Program
{
static DateTime endTime;
static void SetEndTime(DateTime date)
{
endTime = date;
}
static async Task Main(string[] args)
{
var configurationBuilder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: false);
IConfiguration configuration = configurationBuilder.Build();
AppSettings AppSettings = configuration.GetSection("AppSettings").Get<AppSettings>();
// Setup DI
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IConfigurationService, ConfigurationService>();
serviceCollection.AddSingleton<IPipefyApiService>(provider => {
var config = provider.GetRequiredService<IConfigurationService>().LoadAppSettings();
return new PipefyApiService(config.PIPEFY_API_URL, config.PIPEFY_API_TOKEN);
});
serviceCollection.AddSingleton<IDatabaseService, DatabaseService>();
serviceCollection.AddSingleton<IDataMapper, DataMapper>();
serviceCollection.AddSingleton<IBusinessLogicService, BusinessLogicService>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var configService = serviceProvider.GetRequiredService<IConfigurationService>();
var AppSettings = configService.LoadAppSettings();
if (AppSettings is null){ Environment.Exit(1); }
Console.Clear();
// URL da API que você deseja chamar
string ConnSourcePath = AppSettings.DB_PATH;
string apiUrl = AppSettings.PIPEFY_API_URL;
string PipefyToken = AppSettings.PIPEFY_API_TOKEN;
string PipefyTokenTableID = AppSettings.PIPEFY_TABLE_ID;
string PipefyTokenTableIDGestores = AppSettings.PIPEFY_TABLE_ID_GESTORES;
JArray allRecords = await GetPipefyDataAsync(apiUrl,PipefyToken,PipefyTokenTableID);
JArray allGestores = await GetPipefyDataAsync(apiUrl, PipefyToken, PipefyTokenTableIDGestores);
var pipefyApi = serviceProvider.GetRequiredService<IPipefyApiService>();
JArray allRecords = await pipefyApi.GetRecordsAsync(AppSettings.PIPEFY_TABLE_ID);
JArray allGestores = await pipefyApi.GetGestoresAsync(AppSettings.PIPEFY_TABLE_ID_GESTORES);
Console.Clear();
if (allRecords is not null)
{
string strGestores = JsonConvert.SerializeObject(allGestores);
// Desserialize o JSON em objetos C#
var jsonGestores = JsonConvert.DeserializeObject<List<Pipefy.RootGestor>>(strGestores);
List<Pipefy.ClasseGestores> jsonListGestores = convertGestoresJson(jsonGestores);
var jsonGestores = JsonConvert.DeserializeObject<List<RootGestor>>(strGestores)!;
var mapper = serviceProvider.GetRequiredService<IDataMapper>();
List<ClasseGestores> jsonListGestores = mapper.ConvertGestoresJson(jsonGestores);
string strJson = JsonConvert.SerializeObject(allRecords);
// Desserialize o JSON em objetos C#
var jsonData = JsonConvert.DeserializeObject<List<Pipefy.RootObject>>(strJson);
List<Pipefy.ClasseEmpresas> jsonList = convertEmpresasJson(jsonData);
// Consulte os dados da tabela no banco de dados Access
List<Pipefy.ClasseEmpresas> databaseData = GetDataFromDatabase(ConnSourcePath);
// Compare os dados e encontre os registros ausentes no JSON
List<Pipefy.ClasseEmpresas> recordsMissingInJson = CompareData(databaseData, jsonList, jsonListGestores);
if (recordsMissingInJson.Count != 0)
var jsonData = JsonConvert.DeserializeObject<List<RootObject>>(strJson)!;
List<ClasseEmpresas> jsonList = mapper.ConvertEmpresasJson(jsonData);
var databaseService = serviceProvider.GetRequiredService<IDatabaseService>();
List<ClasseEmpresas> databaseData = databaseService.GetDataFromDatabase(AppSettings.DB_PATH);
var businessLogic = serviceProvider.GetRequiredService<IBusinessLogicService>();
List<ClasseEmpresas> recordsMissingInJson = businessLogic.CompareData(databaseData, jsonList, jsonListGestores);
if (recordsMissingInJson.Count != 0 && recordsMissingInJson != null)
{
string strQuery = "{\"query\":\"mutation {\\r\\n ";
for (int i = 0; i < recordsMissingInJson.Count; i++)
{
if (i % 49 == 0) { strQuery = "{\"query\":\"mutation {\\r\\n "; }
strQuery = strQuery + $"N{i}: createTableRecord(input: {{ table_id: \\\"{PipefyTokenTableID}\\\", fields_attributes: [ {{ field_id: \\\"nome_da_empresa\\\", field_value: \\\"{recordsMissingInJson[i].nome_da_empresa.ToString()}\\\" }} {{ field_id: \\\"c_digo_smart\\\", field_value: \\\"{recordsMissingInJson[i].c_digo_smart.ToString()}\\\" }} {{ field_id: \\\"modalidade\\\", field_value: \\\"{recordsMissingInJson[i].modalidade.ToString()}\\\" }} {{ field_id: \\\"gestores\\\", field_value: \\\"{recordsMissingInJson[i].gestores.ToString()}\\\" }}]}}) {{ table_record {{ id }}}}\\r\\n ";
if (i % 48 == 0 && i != 0) {
strQuery = strQuery + "}\",\"variables\":{}}";
bool success = await CreatePipefyDataAsync(apiUrl, PipefyToken, PipefyTokenTableID, strQuery);
//if (!success) {
// int test2 = 10;
//}
}
}
strQuery = strQuery + "}\",\"variables\":{}}";
bool success1 = await CreatePipefyDataAsync(apiUrl, PipefyToken, PipefyTokenTableID, strQuery);
// Faça algo com os registros ausentes
int maxCId = recordsMissingInJson.OrderByDescending(s => s.c_digo_smart.Length).First().c_digo_smart.Length;
int maxCNome = recordsMissingInJson.OrderByDescending(s => s.nome_da_empresa.Length).First().nome_da_empresa.Length;
int maxCMod = recordsMissingInJson.OrderByDescending(s => s.modalidade.Length).First().modalidade.Length;
int maxCGestao = recordsMissingInJson.OrderByDescending(s => s.gestores.Length).First().gestores.Length;
await pipefyApi.CreateRecordsAsync(AppSettings.PIPEFY_TABLE_ID, recordsMissingInJson);
int maxCId = recordsMissingInJson.OrderByDescending(s => s.c_digo_smart!.Length).First().c_digo_smart!.Length;
int maxCNome = recordsMissingInJson.OrderByDescending(s => s.nome_da_empresa!.Length).First().nome_da_empresa!.Length;
int maxCMod = recordsMissingInJson.OrderByDescending(s => s.modalidade!.Length).First().modalidade!.Length;
int maxCGestao = recordsMissingInJson.OrderByDescending(s => s.gestores!.Length).First().gestores!.Length;
foreach (var record in recordsMissingInJson)
{
Console.WriteLine(String.Format($"| ID: {{0,{maxCId}}} | Nome: {{1,{maxCNome}}} | Modalidade: {{2,{maxCMod}}} | Gestão: {{3,{maxCGestao}}} |",record.c_digo_smart,record.nome_da_empresa,record.modalidade,record.gestores));
}
Console.WriteLine($"");
Console.WriteLine("");
}
Console.WriteLine($"{recordsMissingInJson!.Count} registros encontrados.");
}
Console.WriteLine($"{recordsMissingInJson.Count} registros encontrados.");
var timer = new System.Timers.Timer(100);
double remainingTime;
int interval = 60;
SetEndTime(System.DateTime.Now.AddSeconds(interval));
timer.Elapsed += OnTimerElapsed!;
timer.AutoReset = true;
timer.Enabled = true;
Console.WriteLine("");
Console.WriteLine("Pressione qualquer tecla para encerrar o programa...");
Console.WriteLine("");
Task.Factory.StartNew(
() => {
Console.ReadKey();
remainingTime = DateTime.Now.Subtract(endTime).TotalSeconds;
}
System.Threading.Thread.Sleep(10000);
).Wait(
TimeSpan.FromSeconds(interval)
);
}
private static async Task<bool> CreatePipefyDataAsync(string apiUrl, string PipefyToken, string PipefyTokenTableID, string query)
private static void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
var httpClient = new HttpClient();
// Defina os headers da requisição (opcional)
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + PipefyToken);
var request = new HttpRequestMessage(HttpMethod.Post, apiUrl);
request.Headers.Add("Accept", "application/json");
var content = new StringContent(query, null, "application/json");
request.Content = content;
var response = await httpClient.SendAsync(request);
var body = JsonConvert.SerializeObject(response.Content);
return response.IsSuccessStatusCode;
}
private static async Task<JArray> GetPipefyDataAsync(string apiUrl, string PipefyToken, string PipefyTokenTableID)
{
JArray allRecords = new JArray();
string cursor = "null";
string query = $"{{\"query\":\"query GetRecords($cursor: String){{ table_records(table_id: \\\"{PipefyTokenTableID}\\\",first:50,after:$cursor){{ pageInfo{{ hasNextPage endCursor }} edges{{ node{{ id record_fields{{ field {{ id }} value array_value }} }} }} }}}}\",\"variables\":{{\"cursor\":{cursor}}}}}";
bool hasNextPage = true;
while (hasNextPage)
{
var httpClient = new HttpClient();
// Defina os headers da requisição (opcional)
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + PipefyToken);
var request = new HttpRequestMessage(HttpMethod.Post, apiUrl);
request.Headers.Add("Accept", "application/json");
var content = new StringContent(query, null, "application/json");
request.Content = content;
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var responseData = JObject.Parse(responseContent);
Console.Clear();
var records = responseData["data"]["table_records"]["edges"];
foreach (var record in records)
{
//Console.WriteLine(record);
allRecords.Add(record);
}
hasNextPage = responseData["data"]["table_records"]["pageInfo"]["hasNextPage"].Value<bool>();
cursor = responseData["data"]["table_records"]["pageInfo"]["endCursor"].Value<string>();
query = $"{{\"query\":\"query GetRecords($cursor: String){{ table_records(table_id: \\\"{PipefyTokenTableID}\\\",first:50,after:$cursor){{ pageInfo{{ hasNextPage endCursor }} edges{{ node{{ record_fields{{ field {{ id }} value array_value }} }} }} }}}}\",\"variables\":{{\"cursor\":\"{cursor}\"}}}}";
}
else
{
Console.WriteLine($"Erro na solicitação GraphQL: {response.StatusCode}");
allRecords.Clear();
return allRecords;
}
}
return allRecords;
}
static List<Pipefy.ClasseEmpresas> GetDataFromDatabase(string ConnSourcePath)
{
string connectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + ConnSourcePath + ";Jet OLEDB:Database Password=gds21;";
List<Pipefy.ClasseEmpresas> data = new List<Pipefy.ClasseEmpresas>();
using (OleDbConnection connection = new OleDbConnection(connectionString))
{
connection.Open();
// Execute uma consulta SQL para recuperar dados da tabela no banco de dados Access
StringBuilder sqlQuery = new StringBuilder();
sqlQuery.Append("SELECT cod_smart_cliente, \n");
sqlQuery.Append(" cliente, \n");
sqlQuery.Append(" modalidade, \n");
sqlQuery.Append(" gestao \n");
sqlQuery.Append("FROM dados_cadastrais \n");
sqlQuery.Append("WHERE cod_smart_unidade LIKE \"%001\" \n");
sqlQuery.Append(" AND unidade_gerenciada;");
using (OleDbCommand command = new OleDbCommand(sqlQuery.ToString(), connection))
{
using (OleDbDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Pipefy.ClasseEmpresas record = new Pipefy.ClasseEmpresas
{
c_digo_smart = (string)reader["Cod_Smart_cliente"].ToString(),
nome_da_empresa = (string)reader["Cliente"],
modalidade = (string)reader["Modalidade"],
gestores = (string)reader["Gestao"],
rec_id = ""
// Adicione outras propriedades conforme necessário
};
data.Add(record);
}
}
}
}
return data;
}
static List<Pipefy.ClasseEmpresas> convertEmpresasJson(List<Pipefy.RootObject> jsonData)
{
List<Pipefy.ClasseEmpresas> data = new List<Pipefy.ClasseEmpresas>();
for (int i = 0;i<jsonData.Count;i++)
{
Pipefy.ClasseEmpresas record = new Pipefy.ClasseEmpresas();
record.rec_id = jsonData[i].node.id;
for (int j = 0;j < jsonData[i].node.record_fields.Length;j++)
{
switch (jsonData[i].node.record_fields[j].field.id)
{
case "nome_da_empresa":
record.nome_da_empresa = jsonData[i].node.record_fields[j].value;
break;
case "c_digo_smart":
record.c_digo_smart = jsonData[i].node.record_fields[j].value.Replace(".0","");
break;
case "modalidade":
record.modalidade = jsonData[i].node.record_fields[j].value;
break;
case "gestores":
record.gestores = jsonData[i].node.record_fields[j].array_value.FirstOrDefault().ToString();
break;
}
}
// Adicione outras propriedades conforme necessário
data.Add(record);
}
return data;
}
static List<Pipefy.ClasseGestores> convertGestoresJson(List<Pipefy.RootGestor> jsonData)
{
List<Pipefy.ClasseGestores> data = new List<Pipefy.ClasseGestores>();
for (int i = 0; i < jsonData.Count; i++)
{
Pipefy.ClasseGestores record = new Pipefy.ClasseGestores();
record.id = jsonData[i].node.id.ToString();
for (int j = 0; j < jsonData[i].node.record_fields.Length; j++)
{
if (jsonData[i].node.record_fields[j].field.id == "gest_o")
{
record.gestores = jsonData[i].node.record_fields[j].value;
}
}
// Adicione outras propriedades conforme necessário
data.Add(record);
}
return data;
}
static List<Pipefy.ClasseEmpresas> CompareData(List<Pipefy.ClasseEmpresas> databaseData, List<Pipefy.ClasseEmpresas> jsonList, List<Pipefy.ClasseGestores> jsonListGestores)
{
if (jsonList == null | jsonListGestores == null)
{
return databaseData;
}
List<Pipefy.ClasseEmpresas> recordsMissingInJson = new List<Pipefy.ClasseEmpresas>();
var exists = false;
foreach (var record in databaseData)
{
exists = false;
for (var j = 0; j < jsonList.Count; j++) {
//Console.WriteLine(jsonList[j].c_digo_smart.ToString().Replace(".0", "") + " - " + record.c_digo_smart.ToString() + " - " + (jsonList[j].c_digo_smart.ToString().Replace(".0", "") == record.c_digo_smart.ToString()));
if (jsonList[j].c_digo_smart.ToString().Replace(".0", "") == record.c_digo_smart.ToString())
{
exists = true;
break;
}
}
Console.Clear();
if (exists == false) {
record.gestores = findGestores(record.gestores,jsonListGestores);
if (record.gestores != "0")
{
recordsMissingInJson.Add(record);
}
//else
//{
// int test = 10;
//}
}
}
return recordsMissingInJson;
}
static string findGestores(string sCodigo, List<Pipefy.ClasseGestores> jsonListGestores)
{
for (var i = 0; i < jsonListGestores.Count; i++)
{
if (sCodigo == jsonListGestores[i].gestores.ToString())
{
return jsonListGestores[i].id;
}
}
return "0";
Console.Write($"\rEncerrando em {endTime.Subtract(e.SignalTime).Seconds.ToString()}");
}
}

64
SOLID-Refactoring-Plan.md Normal file
View File

@ -0,0 +1,64 @@
# SOLID Refactoring Plan for Pipefy Project
This document outlines a step-by-step plan to refactor the Pipefy project to comply with SOLID principles. Check off each step as you complete it.
---
## 1. Preparation
- [x] Review current codebase and identify major responsibilities in `Program.cs` and `data.cs`.
- **Program.cs**: Handles configuration loading, user interaction, API communication, JSON deserialization, database access, data comparison, record creation, console output, timer logic, and orchestration.
- **data.cs**: Contains data models for API responses, business entities, and configuration. No business logic.
- [x] List all external dependencies (API, database, configuration).
- **Pipefy API**: Used for fetching and creating records (GraphQL over HTTP, requires API token and table IDs).
- **Access Database**: Used for reading local records (via OleDb, requires DB path and password).
- **Configuration**: Loaded from `appsettings.json` (contains DB path, API token, API URL, table IDs).
- **NuGet Packages**: Microsoft.Extensions.Configuration, Newtonsoft.Json, System.Data.OleDb, etc.
## 2. Project Structure
- [x] Create folders: `Services/`, `Models/`, `Mappers/` (if needed).
- `Services/`: For service and interface implementations (API, DB, config, business logic).
- `Models/`: For data models (move from `data.cs`).
- `Mappers/`: For mapping logic between API/DB and domain models (optional, if mapping grows).
- [ ] Integrate ORM (e.g., Entity Framework Core) to abstract DB access and ease future DB changes. (Next step)
- [x] Move or create files as needed for separation of concerns.
## 3. Single Responsibility Principle (SRP)
- [x] Extract configuration loading into `IConfigurationService` and `ConfigurationService`.
- [x] Extract Pipefy API logic into `IPipefyApiService` and `PipefyApiService`.
- [x] Extract database logic into `IDatabaseService` and `DatabaseService`.
- [x] Extract data mapping logic into `IDataMapper` and `DataMapper`.
- [x] Extract business logic (comparison, orchestration) into `IBusinessLogicService` and `BusinessLogicService`.
- [x] Remove static business/data methods from `Program.cs` and ensure all logic is in services.
## 4. Open/Closed Principle (OCP)
- [x] Define interfaces for each service (already done: IConfigurationService, IPipefyApiService, IDatabaseService, IDataMapper, IBusinessLogicService).
- [x] Ensure new data sources or logic can be added by implementing new classes, not modifying existing ones (all main logic is now behind interfaces and DI).
## 5. Liskov Substitution Principle (LSP)
- [ ] Ensure all service implementations can be replaced by their interfaces without breaking functionality.
## 6. Interface Segregation Principle (ISP)
- [ ] Keep interfaces focused and small.
- [ ] Split large interfaces if needed.
## 7. Dependency Inversion Principle (DIP)
- [x] Refactor `Program.cs` to depend on abstractions (interfaces), not concrete classes.
- [x] Use dependency injection to provide services to the main program.
## 8. Testing
- [ ] Add or update unit tests for each service.
- [ ] Ensure business logic is testable in isolation.
## 9. Documentation
- [ ] Update this plan as you progress.
- [ ] Document new structure and usage in a `README.md` or similar file.
---
**Progress Tracking:**
- Mark each step as complete (`[x]`) as you finish it.
- Add notes or decisions below each step if needed.
---
_Last updated: May 16, 2025_

View File

@ -0,0 +1,50 @@
using System.Collections.Generic;
using Pipefy.Models;
namespace Pipefy.Services
{
public class BusinessLogicService : IBusinessLogicService
{
public List<ClasseEmpresas> CompareData(List<ClasseEmpresas> databaseData, List<ClasseEmpresas> jsonList, List<ClasseGestores> jsonListGestores)
{
if (jsonList == null || jsonListGestores == null)
{
return databaseData;
}
List<ClasseEmpresas> recordsMissingInJson = new List<ClasseEmpresas>();
foreach (var record in databaseData)
{
bool exists = false;
for (var j = 0; j < jsonList.Count; j++)
{
if (jsonList[j].c_digo_smart!.ToString().Replace(".0", "") == record.c_digo_smart!.ToString())
{
exists = true;
break;
}
}
if (!exists)
{
record.gestores = FindGestores(record.gestores!, jsonListGestores);
if (record.gestores != "0")
{
recordsMissingInJson.Add(record);
}
}
}
return recordsMissingInJson;
}
public string FindGestores(string sCodigo, List<ClasseGestores> jsonListGestores)
{
for (var i = 0; i < jsonListGestores.Count; i++)
{
if (sCodigo == jsonListGestores[i].gestores!.ToString())
{
return jsonListGestores[i].id!;
}
}
return "0";
}
}
}

View File

@ -0,0 +1,30 @@
using Microsoft.Extensions.Configuration;
using Pipefy.Models;
namespace Pipefy.Services
{
public class ConfigurationService : IConfigurationService
{
private readonly IConfiguration _configuration;
public ConfigurationService()
{
_configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false)
.Build();
}
public AppSettings GetAppSettings()
{
return _configuration.GetSection("AppSettings").Get<AppSettings>()!;
}
public AppSettings LoadAppSettings()
{
var configurationBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);
IConfiguration configuration = configurationBuilder.Build();
return configuration.GetSection("AppSettings").Get<AppSettings>()!;
}
}
}

57
Services/DataMapper.cs Normal file
View File

@ -0,0 +1,57 @@
using System.Collections.Generic;
using Pipefy.Models;
namespace Pipefy.Services
{
public class DataMapper : IDataMapper
{
public List<ClasseEmpresas> ConvertEmpresasJson(List<RootObject> jsonData)
{
List<ClasseEmpresas> data = new List<ClasseEmpresas>();
for (int i = 0; i < jsonData.Count; i++)
{
ClasseEmpresas record = new ClasseEmpresas();
record.rec_id = jsonData[i].node.id;
for (int j = 0; j < jsonData[i].node.record_fields.Length; j++)
{
switch (jsonData[i].node.record_fields[j].field.id)
{
case "nome_da_empresa":
record.nome_da_empresa = jsonData[i].node.record_fields[j].value;
break;
case "c_digo_smart":
record.c_digo_smart = jsonData[i].node.record_fields[j].value!.Replace(".0", "");
break;
case "modalidade":
record.modalidade = jsonData[i].node.record_fields[j].value;
break;
case "gestores":
record.gestores = jsonData[i].node.record_fields[j].array_value.FirstOrDefault()!.ToString();
break;
}
}
data.Add(record);
}
return data;
}
public List<ClasseGestores> ConvertGestoresJson(List<RootGestor> jsonData)
{
List<ClasseGestores> data = new List<ClasseGestores>();
for (int i = 0; i < jsonData.Count; i++)
{
ClasseGestores record = new ClasseGestores();
record.id = jsonData[i].node.id!.ToString();
for (int j = 0; j < jsonData[i].node.record_fields.Length; j++)
{
if (jsonData[i].node.record_fields[j].field.id == "gest_o")
{
record.gestores = jsonData[i].node.record_fields[j].value;
}
}
data.Add(record);
}
return data;
}
}
}

View File

@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Data.OleDb;
using System.Text;
using Pipefy.Models;
namespace Pipefy.Services
{
public class DatabaseService : IDatabaseService
{
public List<ClasseEmpresas> GetDataFromDatabase(string connSourcePath)
{
string connectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + connSourcePath + ";Jet OLEDB:Database Password=gds21;";
List<ClasseEmpresas> data = new List<ClasseEmpresas>();
using (OleDbConnection connection = new OleDbConnection(connectionString))
{
connection.Open();
StringBuilder sqlQuery = new StringBuilder();
sqlQuery.Append("SELECT cod_smart_cliente, \n");
sqlQuery.Append(" cliente, \n");
sqlQuery.Append(" modalidade, \n");
sqlQuery.Append(" gestao \n");
sqlQuery.Append("FROM dados_cadastrais \n");
sqlQuery.Append("WHERE cod_smart_unidade LIKE \"%001\" \n");
sqlQuery.Append(" AND unidade_gerenciada;");
using (OleDbCommand command = new OleDbCommand(sqlQuery.ToString(), connection))
{
using (OleDbDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
ClasseEmpresas record = new ClasseEmpresas
{
c_digo_smart = reader["Cod_Smart_cliente"].ToString(),
nome_da_empresa = reader["Cliente"].ToString(),
modalidade = reader["Modalidade"].ToString(),
gestores = reader["Gestao"].ToString(),
rec_id = ""
};
data.Add(record);
}
}
}
}
return data;
}
}
}

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
using Pipefy.Models;
namespace Pipefy.Services
{
public interface IBusinessLogicService
{
List<ClasseEmpresas> CompareData(List<ClasseEmpresas> databaseData, List<ClasseEmpresas> jsonList, List<ClasseGestores> jsonListGestores);
string FindGestores(string sCodigo, List<ClasseGestores> jsonListGestores);
}
}

View File

@ -0,0 +1,10 @@
using Pipefy.Models;
namespace Pipefy.Services
{
public interface IConfigurationService
{
AppSettings GetAppSettings();
AppSettings LoadAppSettings();
}
}

11
Services/IDataMapper.cs Normal file
View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
using Pipefy.Models;
namespace Pipefy.Services
{
public interface IDataMapper
{
List<ClasseEmpresas> ConvertEmpresasJson(List<RootObject> jsonData);
List<ClasseGestores> ConvertGestoresJson(List<RootGestor> jsonData);
}
}

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
using Pipefy.Models;
namespace Pipefy.Services
{
public interface IDatabaseService
{
List<ClasseEmpresas> GetDataFromDatabase(string connSourcePath);
}
}

View File

@ -0,0 +1,14 @@
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
using Pipefy.Models;
namespace Pipefy.Services
{
public interface IPipefyApiService
{
Task<JArray> GetRecordsAsync(string tableId);
Task<JArray> GetGestoresAsync(string tableId);
Task<bool> CreateRecordsAsync(string tableId, List<ClasseEmpresas> records);
}
}

View File

@ -0,0 +1,97 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Pipefy.Models;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Pipefy.Services
{
public class PipefyApiService : IPipefyApiService
{
private readonly string _apiUrl;
private readonly string _token;
public PipefyApiService(string apiUrl, string token)
{
_apiUrl = apiUrl;
_token = token;
}
public async Task<JArray> GetRecordsAsync(string tableId)
{
JArray allRecords = new JArray();
string cursor = "null";
string query = $"{{\"query\":\"query GetRecords($cursor: String){{ table_records(table_id: \\\"{tableId}\\\",first:50,after:$cursor){{ pageInfo{{ hasNextPage endCursor }} edges{{ node{{ id record_fields{{ field {{ id }} value array_value }} }} }} }}}}\",\"variables\":{{\"cursor\":{cursor}}}}}";
bool hasNextPage = true;
while (hasNextPage)
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + _token);
var request = new HttpRequestMessage(HttpMethod.Post, _apiUrl);
request.Headers.Add("Accept", "application/json");
var content = new StringContent(query, null, "application/json");
request.Content = content;
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var responseData = JObject.Parse(responseContent);
var records = responseData["data"]!["table_records"]!["edges"];
foreach (var record in records!)
{
allRecords.Add(record);
}
hasNextPage = responseData["data"]!["table_records"]!["pageInfo"]!["hasNextPage"]!.Value<bool>();
cursor = responseData["data"]!["table_records"]!["pageInfo"]!["endCursor"]!.Value<string>()!;
query = $"{{\"query\":\"query GetRecords($cursor: String){{ table_records(table_id: \\\"{tableId}\\\",first:50,after:$cursor){{ pageInfo{{ hasNextPage endCursor }} edges{{ node{{ record_fields{{ field {{ id }} value array_value }} }} }} }}}}\",\"variables\":{{\"cursor\":\"{cursor}\"}}}}";
}
else
{
allRecords.Clear();
return allRecords;
}
}
return allRecords;
}
public async Task<JArray> GetGestoresAsync(string tableId)
{
// For now, this is the same as GetRecordsAsync, but can be customized if needed
return await GetRecordsAsync(tableId);
}
public async Task<bool> CreateRecordsAsync(string tableId, List<ClasseEmpresas> records)
{
// This method should build the GraphQL mutation and send it in batches
// For brevity, this is a simplified version
if (records == null || records.Count == 0) return true;
string strQuery = "{\"query\":\"mutation {\\r\\n ";
for (int i = 0; i < records.Count; i++)
{
if (i % 49 == 0) { strQuery = "{\"query\":\"mutation {\\r\\n "; }
strQuery = strQuery + $"N{i}: createTableRecord(input: {{ table_id: \\\"{tableId}\\\", fields_attributes: [ {{ field_id: \\\"nome_da_empresa\\\", field_value: \\\"{records[i].nome_da_empresa}\\\" }} {{ field_id: \\\"c_digo_smart\\\", field_value: \\\"{records[i].c_digo_smart}\\\" }} {{ field_id: \\\"modalidade\\\", field_value: \\\"{records[i].modalidade}\\\" }} {{ field_id: \\\"gestores\\\", field_value: \\\"{records[i].gestores}\\\" }}]}}) {{ table_record {{ id }}}}\\r\\n ";
if (i % 48 == 0 && i != 0)
{
strQuery = strQuery + "}\",\"variables\":{}}";
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + _token);
var request = new HttpRequestMessage(HttpMethod.Post, _apiUrl);
request.Headers.Add("Accept", "application/json");
var content = new StringContent(strQuery, null, "application/json");
request.Content = content;
var response = await httpClient.SendAsync(request);
}
}
strQuery = strQuery + "}\",\"variables\":{}}";
var finalClient = new HttpClient();
finalClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + _token);
var finalRequest = new HttpRequestMessage(HttpMethod.Post, _apiUrl);
finalRequest.Headers.Add("Accept", "application/json");
var finalContent = new StringContent(strQuery, null, "application/json");
finalRequest.Content = finalContent;
var finalResponse = await finalClient.SendAsync(finalRequest);
return finalResponse.IsSuccessStatusCode;
}
}
}

1
Services/README.md Normal file
View File

@ -0,0 +1 @@
// This folder will contain service interfaces and implementations (API, DB, config, business logic)

64
data.cs
View File

@ -5,67 +5,9 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
// All model classes have been moved to Models/Entities.cs under Pipefy.Models namespace.
// This file should now only contain logic or be removed if not needed.
namespace Pipefy
{
public class RootObject
{
public Node node { get; set; }
}
public class RootGestor
{
public NodeGestor node { get; set; }
}
public class Data
{
public Node node { get; set; }
}
public class Node
{
public string id { get; set; }
public Record_Fields[] record_fields { get; set; }
}
public class NodeGestor
{
public string ?id { get; set; }
public Record_Fields[] record_fields { get; set; }
}
public class Record_Fields
{
public Field field { get; set; }
public string ?value { get; set; }
public string[] array_value { get; set; }
}
public class Field
{
public string ?id { get; set; }
}
public class ClasseEmpresas
{
public string ?c_digo_smart { get; set; }
public string ?nome_da_empresa { get; set; }
public string ?modalidade { get; set; }
public string ?gestores { get; set; }
public string rec_id { get; set; }
}
public class ClasseGestores
{
public string ?id { get; set; }
public string ?gestores { get; set; }
}
public class AppSettings
{
public string DB_PATH { get; set; }
public string PIPEFY_API_TOKEN { get; set; }
public string PIPEFY_API_URL { get; set; }
public string PIPEFY_TABLE_ID { get; set; }
public string PIPEFY_TABLE_ID_GESTORES { get; set; }
}
}