diff --git a/.gitignore b/.gitignore index 9491a2f..a724052 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,7 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd + +# History for Visual Studio +.history/ diff --git a/Mappers/README.md b/Mappers/README.md new file mode 100644 index 0000000..c2ffa44 --- /dev/null +++ b/Mappers/README.md @@ -0,0 +1 @@ +// This folder will contain mapping logic between API/DB and domain models if needed diff --git a/Models/Entities.cs b/Models/Entities.cs new file mode 100644 index 0000000..a7f1b5b --- /dev/null +++ b/Models/Entities.cs @@ -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; } + } +} diff --git a/Models/README.md b/Models/README.md new file mode 100644 index 0000000..c95a6fc --- /dev/null +++ b/Models/README.md @@ -0,0 +1 @@ +// This folder will contain all data models (move from data.cs) diff --git a/Pipefy.csproj b/Pipefy.csproj index 127a3b7..9fe459a 100644 --- a/Pipefy.csproj +++ b/Pipefy.csproj @@ -2,13 +2,19 @@ Exe - net7.0-windows + net8.0-windows enable enable 1b2d76eb-7ab5-428f-9f67-13b6bf96375d + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/Program.cs b/Program.cs index 9d39f56..60e148d 100644 --- a/Program.cs +++ b/Program.cs @@ -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(); + // Setup DI + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(provider => { + var config = provider.GetRequiredService().LoadAppSettings(); + return new PipefyApiService(config.PIPEFY_API_URL, config.PIPEFY_API_TOKEN); + }); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + var serviceProvider = serviceCollection.BuildServiceProvider(); + var configService = serviceProvider.GetRequiredService(); + 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(); + 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>(strGestores); - - List jsonListGestores = convertGestoresJson(jsonGestores); - + var jsonGestores = JsonConvert.DeserializeObject>(strGestores)!; + var mapper = serviceProvider.GetRequiredService(); + List jsonListGestores = mapper.ConvertGestoresJson(jsonGestores); string strJson = JsonConvert.SerializeObject(allRecords); - // Desserialize o JSON em objetos C# - var jsonData = JsonConvert.DeserializeObject>(strJson); - - List jsonList = convertEmpresasJson(jsonData); - - // Consulte os dados da tabela no banco de dados Access - List databaseData = GetDataFromDatabase(ConnSourcePath); - - // Compare os dados e encontre os registros ausentes no JSON - List recordsMissingInJson = CompareData(databaseData, jsonList, jsonListGestores); - - if (recordsMissingInJson.Count != 0) + var jsonData = JsonConvert.DeserializeObject>(strJson)!; + List jsonList = mapper.ConvertEmpresasJson(jsonData); + var databaseService = serviceProvider.GetRequiredService(); + List databaseData = databaseService.GetDataFromDatabase(AppSettings.DB_PATH); + var businessLogic = serviceProvider.GetRequiredService(); + List 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."); } - System.Threading.Thread.Sleep(10000); + + 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; + } + ).Wait( + TimeSpan.FromSeconds(interval) + ); } - private static async Task 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 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(); - cursor = responseData["data"]["table_records"]["pageInfo"]["endCursor"].Value(); - 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 GetDataFromDatabase(string ConnSourcePath) - { - string connectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + ConnSourcePath + ";Jet OLEDB:Database Password=gds21;"; - List data = new List(); - - 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 convertEmpresasJson(List jsonData) - { - List data = new List(); - - for (int i = 0;i convertGestoresJson(List jsonData) - { - List data = new List(); - - 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 CompareData(List databaseData, List jsonList, List jsonListGestores) - { - if (jsonList == null | jsonListGestores == null) - { - return databaseData; - } - - List recordsMissingInJson = new List(); - 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 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()}"); } } \ No newline at end of file diff --git a/SOLID-Refactoring-Plan.md b/SOLID-Refactoring-Plan.md new file mode 100644 index 0000000..f483285 --- /dev/null +++ b/SOLID-Refactoring-Plan.md @@ -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_ diff --git a/Services/BusinessLogicService.cs b/Services/BusinessLogicService.cs new file mode 100644 index 0000000..2a8328f --- /dev/null +++ b/Services/BusinessLogicService.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using Pipefy.Models; + +namespace Pipefy.Services +{ + public class BusinessLogicService : IBusinessLogicService + { + public List CompareData(List databaseData, List jsonList, List jsonListGestores) + { + if (jsonList == null || jsonListGestores == null) + { + return databaseData; + } + List recordsMissingInJson = new List(); + 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 jsonListGestores) + { + for (var i = 0; i < jsonListGestores.Count; i++) + { + if (sCodigo == jsonListGestores[i].gestores!.ToString()) + { + return jsonListGestores[i].id!; + } + } + return "0"; + } + } +} diff --git a/Services/ConfigurationService.cs b/Services/ConfigurationService.cs new file mode 100644 index 0000000..120441c --- /dev/null +++ b/Services/ConfigurationService.cs @@ -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()!; + } + + public AppSettings LoadAppSettings() + { + var configurationBuilder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false); + IConfiguration configuration = configurationBuilder.Build(); + return configuration.GetSection("AppSettings").Get()!; + } + } +} diff --git a/Services/DataMapper.cs b/Services/DataMapper.cs new file mode 100644 index 0000000..d599ae4 --- /dev/null +++ b/Services/DataMapper.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using Pipefy.Models; + +namespace Pipefy.Services +{ + public class DataMapper : IDataMapper + { + public List ConvertEmpresasJson(List jsonData) + { + List data = new List(); + 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 ConvertGestoresJson(List jsonData) + { + List data = new List(); + 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; + } + } +} diff --git a/Services/DatabaseService.cs b/Services/DatabaseService.cs new file mode 100644 index 0000000..3b76108 --- /dev/null +++ b/Services/DatabaseService.cs @@ -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 GetDataFromDatabase(string connSourcePath) + { + string connectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + connSourcePath + ";Jet OLEDB:Database Password=gds21;"; + List data = new List(); + 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; + } + } +} diff --git a/Services/IBusinessLogicService.cs b/Services/IBusinessLogicService.cs new file mode 100644 index 0000000..8dbf000 --- /dev/null +++ b/Services/IBusinessLogicService.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Pipefy.Models; + +namespace Pipefy.Services +{ + public interface IBusinessLogicService + { + List CompareData(List databaseData, List jsonList, List jsonListGestores); + string FindGestores(string sCodigo, List jsonListGestores); + } +} diff --git a/Services/IConfigurationService.cs b/Services/IConfigurationService.cs new file mode 100644 index 0000000..1a832b2 --- /dev/null +++ b/Services/IConfigurationService.cs @@ -0,0 +1,10 @@ +using Pipefy.Models; + +namespace Pipefy.Services +{ + public interface IConfigurationService + { + AppSettings GetAppSettings(); + AppSettings LoadAppSettings(); + } +} diff --git a/Services/IDataMapper.cs b/Services/IDataMapper.cs new file mode 100644 index 0000000..d0afd4e --- /dev/null +++ b/Services/IDataMapper.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Pipefy.Models; + +namespace Pipefy.Services +{ + public interface IDataMapper + { + List ConvertEmpresasJson(List jsonData); + List ConvertGestoresJson(List jsonData); + } +} diff --git a/Services/IDatabaseService.cs b/Services/IDatabaseService.cs new file mode 100644 index 0000000..e655af1 --- /dev/null +++ b/Services/IDatabaseService.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Pipefy.Models; + +namespace Pipefy.Services +{ + public interface IDatabaseService + { + List GetDataFromDatabase(string connSourcePath); + } +} diff --git a/Services/IPipefyApiService.cs b/Services/IPipefyApiService.cs new file mode 100644 index 0000000..aec092d --- /dev/null +++ b/Services/IPipefyApiService.cs @@ -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 GetRecordsAsync(string tableId); + Task GetGestoresAsync(string tableId); + Task CreateRecordsAsync(string tableId, List records); + } +} diff --git a/Services/PipefyApiService.cs b/Services/PipefyApiService.cs new file mode 100644 index 0000000..2d62b4e --- /dev/null +++ b/Services/PipefyApiService.cs @@ -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 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(); + cursor = responseData["data"]!["table_records"]!["pageInfo"]!["endCursor"]!.Value()!; + 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 GetGestoresAsync(string tableId) + { + // For now, this is the same as GetRecordsAsync, but can be customized if needed + return await GetRecordsAsync(tableId); + } + + public async Task CreateRecordsAsync(string tableId, List 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; + } + } +} diff --git a/Services/README.md b/Services/README.md new file mode 100644 index 0000000..f5a672c --- /dev/null +++ b/Services/README.md @@ -0,0 +1 @@ +// This folder will contain service interfaces and implementations (API, DB, config, business logic) diff --git a/data.cs b/data.cs index 8cb1427..e90ab63 100644 --- a/data.cs +++ b/data.cs @@ -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; } - } }