using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Logging; using CredentialManager.Models; using DataConnection.REST.Interfaces; using DataConnection.REST.Models; using Data_Coupler.Services; namespace Data_Coupler.Pages; /// /// Partial class per la gestione di Salesforce come sorgente dati. /// Consente di autenticarsi a Salesforce, scoprire gli SObject disponibili /// e selezionare un'entità da cui estrarre i dati da trasferire. /// public partial class DataCoupler : ComponentBase { // ===== PROPRIETÀ SALESFORCE SOURCE ===== /// Credenziali Salesforce disponibili come sorgente protected List salesforceSourceCredentials = new(); /// Credenziale Salesforce selezionata come sorgente protected string selectedSalesforceSourceCredential = ""; /// Stato connessione in corso protected bool isConnectingSalesforceSource = false; /// Salesforce source connessa con successo protected bool isSalesforceSourceConnected = false; /// Messaggio di errore connessione Salesforce source protected string salesforceSourceErrorMessage = ""; /// Lista degli SObject Salesforce disponibili (summaries) protected List salesforceSourceEntities = new(); /// SObject Salesforce selezionato come sorgente protected RestEntitySummary? selectedSalesforceSourceEntity = null; /// Dettagli (campi) dell'SObject selezionato protected RestEntityInfo? salesforceSourceEntityDetails = null; /// Termine di ricerca per filtrare gli SObject protected string salesforceSourceSearchTerm = ""; /// Client REST per le operazioni Salesforce source protected IRestServiceClient? currentSalesforceSourceClient = null; /// Discovery metadata per Salesforce source protected IRestMetadataDiscovery? currentSalesforceSourceDiscovery = null; // ===== METODI SALESFORCE SOURCE ===== /// /// Carica le credenziali di tipo Salesforce per usarle come sorgente /// protected async Task LoadSalesforceSourceCredentials() { try { var allCreds = await CredentialService.GetAllRestApiCredentialsAsync(); salesforceSourceCredentials = allCreds .Where(c => c.ServiceType == RestServiceType.Salesforce) .ToList(); Logger.LogInformation("Caricate {Count} credenziali Salesforce per uso come sorgente", salesforceSourceCredentials.Count); } catch (Exception ex) { Logger.LogError(ex, "Errore nel caricamento delle credenziali Salesforce source"); } } /// /// Gestisce il cambio di credenziale Salesforce source /// protected void OnSalesforceSourceCredentialChanged(ChangeEventArgs e) { var newCredential = e.Value?.ToString() ?? ""; // Pulisce la cache se si cambia credenziale if (!string.IsNullOrEmpty(selectedSalesforceSourceCredential) && selectedSalesforceSourceCredential != newCredential) { try { ConnectionFactory.ClearRestClientCache(selectedSalesforceSourceCredential); } catch { /* ignore */ } } selectedSalesforceSourceCredential = newCredential; ResetSalesforceSourceState(); } /// /// Resetta lo stato della connessione Salesforce source /// protected void ResetSalesforceSourceState() { isSalesforceSourceConnected = false; salesforceSourceEntities.Clear(); selectedSalesforceSourceEntity = null; salesforceSourceEntityDetails = null; salesforceSourceSearchTerm = ""; salesforceSourceErrorMessage = ""; currentSalesforceSourceDiscovery = null; currentSalesforceSourceClient = null; } /// /// Si connette a Salesforce come sorgente e scopre gli SObject disponibili /// protected async Task ConnectToSalesforceSource() { if (string.IsNullOrEmpty(selectedSalesforceSourceCredential)) return; isConnectingSalesforceSource = true; salesforceSourceErrorMessage = ""; try { // Verifica la credenziale var credential = salesforceSourceCredentials.FirstOrDefault(c => c.Name == selectedSalesforceSourceCredential); if (credential == null) { salesforceSourceErrorMessage = "Credenziale Salesforce non trovata"; return; } // Crea i client usando il factory currentSalesforceSourceClient = await ConnectionFactory.CreateRestServiceClientAsync(selectedSalesforceSourceCredential); currentSalesforceSourceDiscovery = await ConnectionFactory.CreateRestMetadataDiscoveryAsync(selectedSalesforceSourceCredential); // Autenticazione Logger.LogInformation("Avvio autenticazione Salesforce source: {Credential}", selectedSalesforceSourceCredential); var authResult = await currentSalesforceSourceClient.AuthenticateAsync(); if (!authResult) { salesforceSourceErrorMessage = "Autenticazione Salesforce fallita. Verificare le credenziali."; currentSalesforceSourceClient = null; currentSalesforceSourceDiscovery = null; return; } // Discovery parallela: summaries veloci → UI interattiva subito; dettagli completi in background Logger.LogInformation("Avvio discovery parallela SObject Salesforce source..."); var summariesTask = currentSalesforceSourceDiscovery.DiscoverEntitySummariesAsync(); var entitiesTask = currentSalesforceSourceDiscovery.DiscoverEntitiesAsync(); // Le summaries sono rapide (1 sola API call) → rendiamo la UI interattiva subito salesforceSourceEntities = await summariesTask; isSalesforceSourceConnected = true; StateHasChanged(); Logger.LogInformation("SObject summaries caricate: {Count} entità disponibili", salesforceSourceEntities.Count); // I dettagli completano in background (non bloccano la UI) try { await entitiesTask; Logger.LogInformation("Discovery dettagli SObject completata in background"); } catch (Exception ex) { Logger.LogWarning(ex, "Impossibile completare la discovery dettagli SObject (non critico)"); } } catch (Exception ex) { salesforceSourceErrorMessage = $"Errore di connessione: {ex.Message}"; Logger.LogError(ex, "Errore nella connessione a Salesforce source: {Credential}", selectedSalesforceSourceCredential); currentSalesforceSourceClient = null; currentSalesforceSourceDiscovery = null; } finally { isConnectingSalesforceSource = false; StateHasChanged(); } } /// /// Seleziona un SObject Salesforce come sorgente dati e carica i suoi campi /// protected async Task SelectSalesforceSourceEntity(RestEntitySummary entity) { selectedSalesforceSourceEntity = entity; salesforceSourceEntityDetails = null; // Carica i dettagli dei campi dell'SObject selezionato if (currentSalesforceSourceDiscovery != null) { try { Logger.LogInformation("Caricamento dettagli SObject sorgente: {EntityName}", entity.Name); salesforceSourceEntityDetails = await currentSalesforceSourceDiscovery.DiscoverEntityDetailsAsync(entity.Name); Logger.LogInformation("Dettagli SObject caricati: {FieldCount} campi disponibili", salesforceSourceEntityDetails?.Properties.Count ?? 0); } catch (Exception ex) { Logger.LogError(ex, "Errore nel caricamento dettagli SObject {EntityName}", entity.Name); salesforceSourceErrorMessage = $"Errore nel caricamento dei campi: {ex.Message}"; } } // Pulisce i mapping esistenti quando si cambia entità ClearAllMappings(); StateHasChanged(); } /// /// Restituisce la lista filtrata degli SObject in base al termine di ricerca /// protected IEnumerable GetFilteredSalesforceSourceEntities() { if (string.IsNullOrEmpty(salesforceSourceSearchTerm)) return salesforceSourceEntities; return salesforceSourceEntities .Where(e => e.Name.Contains(salesforceSourceSearchTerm, StringComparison.OrdinalIgnoreCase) || (!string.IsNullOrEmpty(e.Label) && e.Label.Contains(salesforceSourceSearchTerm, StringComparison.OrdinalIgnoreCase))); } /// /// Aggiorna il termine di ricerca per gli SObject sorgente /// protected void FilterSalesforceSourceEntities(ChangeEventArgs e) { salesforceSourceSearchTerm = e.Value?.ToString() ?? ""; StateHasChanged(); } /// /// Pulisce il termine di ricerca per gli SObject sorgente /// protected void ClearSalesforceSourceSearch() { salesforceSourceSearchTerm = ""; StateHasChanged(); } /// /// Estrae tutti i record dall'SObject Salesforce selezionato usando le mappature campi configurate /// protected async Task>> GetAllRecordsFromSalesforceSource() { if (currentSalesforceSourceClient == null || selectedSalesforceSourceEntity == null) return new List>(); if (!(currentSalesforceSourceClient is DataConnection.REST.Implementations.SalesforceServiceClient sfClient)) { Logger.LogError("Il client Salesforce source non è un'istanza di SalesforceServiceClient"); return new List>(); } try { // Determina i campi da estrarre (solo quelli mappati + campo chiave) var fieldsToExtract = new List(); // Aggiungi i campi sorgente dal mapping fieldsToExtract.AddRange(fieldMappings.Keys); // Aggiungi il campo chiave sorgente se configurato if (!string.IsNullOrEmpty(sourceKeyField) && !fieldsToExtract.Contains(sourceKeyField)) fieldsToExtract.Add(sourceKeyField); // Aggiungi i campi usati nelle External ID Relationships (se presenti e destinazione è REST) foreach (var rel in externalIdRelationships) { if (!string.IsNullOrEmpty(rel.SourceField) && !fieldsToExtract.Contains(rel.SourceField)) fieldsToExtract.Add(rel.SourceField); } // Se nessun campo è specificato, estrae tutto var fields = fieldsToExtract.Any() ? fieldsToExtract : null; Logger.LogInformation("Estrazione dati da Salesforce SObject: {EntityName}, Campi: {Fields}", selectedSalesforceSourceEntity.Name, fields != null ? string.Join(", ", fields) : "tutti"); var records = await sfClient.ExtractAllEntitiesAsync( selectedSalesforceSourceEntity.Name, fields); Logger.LogInformation("Estratti {Count} record da Salesforce {EntityName}", records.Count, selectedSalesforceSourceEntity.Name); return records; } catch (Exception ex) { Logger.LogError(ex, "Errore nell'estrazione dati da Salesforce {EntityName}", selectedSalesforceSourceEntity?.Name ?? "N/A"); throw; } } }