feat: refactoring DataCoupler - suddivisione in classi parziali

- Estrazione logica database in DatabaseMethod.cs con tutte le proprietà e metodi protected
- Estrazione logica REST API in RESTMethod.cs con gestione completa delle connessioni
- Creazione DataCouplerModels.cs per modelli condivisi (TransferResult)
- Pulizia file principale DataCoupler.razor.cs mantenendo solo orchestrazione generale
- Rimozione codice duplicato e miglioramento separazione delle responsabilità
- Compilazione verificata senza errori per l'intera soluzione

Struttura finale:
- DataCoupler.razor.cs: gestione file, profili, UI e coordinamento
- DatabaseMethod.cs: connessioni DB, query custom, discovery tabelle/schemi
- RESTMethod.cs: autenticazione REST, discovery entità, metadata
- DataCouplerModels.cs: modelli comuni per
This commit is contained in:
2025-07-13 16:10:58 +02:00
parent 87defc38b8
commit d4e15ab0a7
4 changed files with 1159 additions and 979 deletions
@@ -0,0 +1,882 @@
using System;
using System.Data;
using System.Text;
using CredentialManager.Models;
using CredentialManager.Services;
using DataConnection.Interfaces;
using DataConnection.REST.Interfaces;
using DataConnection.REST.Models;
using DataConnection.CredentialManagement.Interfaces;
using ExcelDataReader;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.JSInterop;
using Microsoft.Extensions.Logging;
using Data_Coupler.Services;
using Data_Coupler.Models;
namespace Data_Coupler.Pages;
public partial class DataCoupler : ComponentBase
{
// ===== PROPRIETÀ DATABASE =====
// Stato delle credenziali database
protected List<DatabaseCredential> databaseCredentials = new();
protected string selectedDatabaseCredential = "";
// Stato connessioni database
protected bool isConnectingDatabase = false;
protected bool isDatabaseConnected = false;
protected string databaseErrorMessage = "";
// Database discovery
protected List<string> availableTableNames = new(); // Solo nomi delle tabelle
protected Dictionary<string, IEnumerable<DbColumnInfo>> databaseTables = new(); // Schema dettagliato per tabelle caricate
protected string selectedTable = "";
protected string databaseSearchTerm = "";
// Database selection - per gestire la selezione del database quando non specificato nella connection string
protected List<string> availableDatabases = new();
protected string selectedDatabase = "";
protected bool showDatabaseSelectionModal = false;
protected bool isLoadingDatabases = false;
// Database selection (schemas only)
protected List<string> availableSchemas = new();
protected string selectedSchema = "";
protected bool showSchemaSelectionModal = false;
protected bool isLoadingSchemas = false;
// Custom query functionality
protected bool useCustomQuery = false;
protected string customQuery = "";
protected bool isValidatingQuery = false;
protected bool isQueryValid = false;
protected string queryValidationMessage = "";
protected List<Dictionary<string, object>> queryPreviewData = new();
protected List<string> queryColumns = new();
protected bool showQueryPreview = false;
protected bool isLoadingPreview = false;
// Gestione chiavi sorgente per database
protected string suggestedPrimaryKey = ""; // Campo PK suggerito per database
// Servizi database
protected IDatabaseManager? currentDatabaseManager = null;
// ===== METODI DATABASE =====
/// <summary>
/// Gestisce il cambio di credenziale database selezionata
/// </summary>
protected void OnDatabaseCredentialChanged(ChangeEventArgs e)
{
selectedDatabaseCredential = e.Value?.ToString() ?? "";
ResetDatabaseState();
}
/// <summary>
/// Resetta lo stato del database
/// </summary>
protected void ResetDatabaseState()
{
isDatabaseConnected = false;
databaseTables.Clear();
selectedTable = "";
databaseSearchTerm = "";
databaseErrorMessage = "";
// Reset database selection
availableDatabases.Clear();
selectedDatabase = "";
showDatabaseSelectionModal = false;
isLoadingDatabases = false;
// Reset custom query state
useCustomQuery = false;
customQuery = "";
isValidatingQuery = false;
isQueryValid = false;
queryValidationMessage = "";
queryPreviewData.Clear();
queryColumns.Clear();
showQueryPreview = false;
isLoadingPreview = false;
currentDatabaseManager?.Dispose();
currentDatabaseManager = null;
// Clear mappings when resetting database state
ClearAllMappings();
}
/// <summary>
/// Connette al database utilizzando le credenziali selezionate
/// </summary>
protected async Task ConnectToDatabase()
{
if (string.IsNullOrEmpty(selectedDatabaseCredential))
return;
isConnectingDatabase = true;
databaseErrorMessage = "";
try
{
// Trova la credenziale
var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential);
if (credential == null)
{
databaseErrorMessage = "Credenziale database non trovata";
return;
}
// Test della connessione
var (success, message) = await CredentialService.TestDatabaseConnectionAsync(credential.Name);
if (!success)
{
databaseErrorMessage = $"Connessione fallita: {message}";
return;
}
// Crea il database manager usando il factory con le credenziali complete
Logger.LogInformation("Creando database manager per credenziale: {CredentialName}", selectedDatabaseCredential);
currentDatabaseManager = await ConnectionFactory.CreateDatabaseManagerAsync(selectedDatabaseCredential);
Logger.LogInformation("Database manager creato con successo");
// Verifica se il database è specificato nella connection string
bool isDatabaseSpecified = await IsDatabaseSpecifiedInConnectionString(credential);
if (isDatabaseSpecified)
{
Logger.LogInformation("Database specificato nella connection string. Procedendo con discovery tabelle.");
try
{
await LoadTablesFromConnectedDatabase();
isDatabaseConnected = true;
Logger.LogInformation("Tabelle caricate con successo, database connesso");
return; // Importante: usciamo qui se tutto va bene
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel caricamento tabelle dal database specificato");
databaseErrorMessage = $"Errore nel caricamento tabelle: {ex.Message}";
return;
}
}
else
{
Logger.LogInformation("Database non specificato nella connection string. Caricando database disponibili.");
await LoadAvailableDatabases();
if (availableDatabases.Any())
{
Logger.LogInformation("Trovati {DatabaseCount} database disponibili", availableDatabases.Count);
showDatabaseSelectionModal = true;
StateHasChanged();
return; // Non procediamo fino alla selezione del database
}
else
{
databaseErrorMessage = "Nessun database disponibile trovato";
return;
}
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nella connessione al database");
databaseErrorMessage = $"Errore: {ex.Message}";
}
finally
{
isConnectingDatabase = false;
StateHasChanged();
}
}
/// <summary>
/// Connette a un database specifico
/// </summary>
protected async Task ConnectToDatabaseWithSpecificDatabase(string databaseName)
{
if (string.IsNullOrEmpty(selectedDatabaseCredential))
return;
isConnectingDatabase = true;
databaseErrorMessage = "";
try
{
// Trova la credenziale
var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential);
if (credential == null)
{
databaseErrorMessage = "Credenziale database non trovata";
return;
}
// Test della connessione
var (success, message) = await CredentialService.TestDatabaseConnectionAsync(credential.Name);
if (!success)
{
databaseErrorMessage = $"Connessione fallita: {message}";
return;
}
// Crea il database manager
Logger.LogInformation("Creando database manager per credenziale: {CredentialName}", selectedDatabaseCredential);
currentDatabaseManager = await ConnectionFactory.CreateDatabaseManagerAsync(selectedDatabaseCredential);
Logger.LogInformation("Database manager creato con successo");
if (currentDatabaseManager == null)
{
databaseErrorMessage = "Database manager non disponibile";
return;
}
Logger.LogInformation("Cambiando database a: {DatabaseName}", databaseName);
// Usa il metodo dell'interfaccia per cambiare database
await currentDatabaseManager.ChangeDatabaseAsync(databaseName);
Logger.LogInformation("Database cambiato con successo a: {DatabaseName}", databaseName);
// Carica le tabelle dal database selezionato
await LoadTablesFromConnectedDatabase();
isDatabaseConnected = true;
Logger.LogInformation("Connessione completata per database: {DatabaseName}", databaseName);
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nella connessione al database specifico: {DatabaseName}", databaseName);
databaseErrorMessage = $"Errore nella connessione al database {databaseName}: {ex.Message}";
throw;
}
finally
{
isConnectingDatabase = false;
StateHasChanged();
}
}
/// <summary>
/// Seleziona una tabella database e carica il suo schema
/// </summary>
protected async Task SelectTable(string tableName)
{
selectedTable = tableName;
// Clear custom query state when selecting a table
useCustomQuery = false;
customQuery = "";
isQueryValid = false;
queryValidationMessage = "";
queryPreviewData.Clear();
queryColumns.Clear();
showQueryPreview = false;
// Clear mappings when changing table
ClearAllMappings();
// Reset key field logic
sourceKeyField = "";
suggestedPrimaryKey = "";
requiresManualKeySelection = false;
// Carica i dettagli della tabella se non sono già stati caricati
if (!databaseTables.ContainsKey(tableName) && currentDatabaseManager != null)
{
try
{
var tableSchema = await currentDatabaseManager.GetTableSchemaAsync(tableName);
databaseTables[tableName] = tableSchema;
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel caricamento dello schema della tabella {TableName}", tableName);
databaseErrorMessage = $"Errore nel caricamento della tabella: {ex.Message}";
}
}
// If it's a database source, try to detect the primary key
if (selectedSourceType == "database" && currentDatabaseManager != null)
{
try
{
var primaryKey = await currentDatabaseManager.GetPrimaryKeyFieldAsync(tableName);
if (!string.IsNullOrEmpty(primaryKey))
{
suggestedPrimaryKey = primaryKey;
// AUTO-SELECT: Imposta automaticamente il campo chiave se rilevato
sourceKeyField = primaryKey;
requiresManualKeySelection = false;
Logger.LogInformation("Chiave primaria rilevata e auto-selezionata per la tabella {TableName}: {PrimaryKey}", tableName, primaryKey);
}
else
{
// No primary key found, require manual selection
requiresManualKeySelection = true;
sourceKeyField = "";
Logger.LogInformation("Nessuna chiave primaria trovata per la tabella {TableName}, selezione manuale richiesta", tableName);
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel rilevamento della chiave primaria per la tabella {TableName}", tableName);
requiresManualKeySelection = true;
sourceKeyField = "";
}
}
else
{
// For non-database sources, always require manual selection
requiresManualKeySelection = true;
sourceKeyField = "";
}
StateHasChanged();
}
/// <summary>
/// Ottiene le tabelle filtrate in base al termine di ricerca
/// </summary>
protected IEnumerable<string> GetFilteredDatabaseTables()
{
if (string.IsNullOrEmpty(databaseSearchTerm))
return availableTableNames;
return availableTableNames.Where(table =>
table.Contains(databaseSearchTerm, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// Filtra le tabelle database in base al termine di ricerca
/// </summary>
protected async Task FilterDatabaseTables(ChangeEventArgs e)
{
databaseSearchTerm = e.Value?.ToString() ?? "";
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// Pulisce il filtro di ricerca delle tabelle database
/// </summary>
protected async Task ClearDatabaseSearch()
{
databaseSearchTerm = "";
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// Seleziona una colonna database per il mapping
/// </summary>
protected void SelectDbColumn(string columnName)
{
selectedDbColumn = columnName;
}
/// <summary>
/// Verifica se il database è specificato nella connection string
/// </summary>
protected Task<bool> IsDatabaseSpecifiedInConnectionString(DatabaseCredential credential)
{
try
{
Logger.LogInformation("Verifica database specificato - Tipo: {DatabaseType}, DatabaseName: '{DatabaseName}', Connection: {ConnectionString}",
credential.DatabaseType, credential.DatabaseName, credential.ConnectionString?.Substring(0, Math.Min(100, credential.ConnectionString?.Length ?? 0)));
// Prima verifica se c'è un database specificato nel campo DatabaseName della credenziale
if (!string.IsNullOrEmpty(credential.DatabaseName))
{
Logger.LogInformation("Database specificato nel campo DatabaseName: '{DatabaseName}' - RESULT: TRUE", credential.DatabaseName);
return Task.FromResult(true);
}
// Per SQL Server verifica se Initial Catalog o Database è specificato nella connection string
if (credential.DatabaseType == DatabaseType.SqlServer)
{
var connectionString = credential.ConnectionString ?? "";
var hasInitialCatalog = connectionString.Contains("Initial Catalog=", StringComparison.OrdinalIgnoreCase);
var hasDatabase = connectionString.Contains("Database=", StringComparison.OrdinalIgnoreCase);
var result = hasInitialCatalog || hasDatabase;
Logger.LogInformation("SQL Server - HasInitialCatalog: {HasInitialCatalog}, HasDatabase: {HasDatabase}, Result: {Result}",
hasInitialCatalog, hasDatabase, result);
return Task.FromResult(result);
}
// TODO: Implementare per altri tipi di database
// MySQL: Database=
// PostgreSQL: Database=
// Oracle: più complesso con SID/Service Name
Logger.LogWarning("Verifica database specificato non implementata per tipo database: {DatabaseType}", credential.DatabaseType);
return Task.FromResult(true); // Default: assume database specificato per tipi non implementati
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nella verifica database specificato in connection string");
return Task.FromResult(true); // Default: assume database specificato in caso di errore
}
}
/// <summary>
/// Carica le tabelle dal database attualmente connesso
/// </summary>
protected async Task LoadTablesFromConnectedDatabase()
{
try
{
if (currentDatabaseManager == null)
{
databaseErrorMessage = "Database manager non disponibile";
return;
}
Logger.LogInformation("Caricando tabelle dal database connesso");
var tableNames = await currentDatabaseManager.GetTableNamesAsync();
availableTableNames = tableNames.ToList();
Logger.LogInformation("Caricate {Count} tabelle dal database", availableTableNames.Count);
// Resetta i dettagli delle tabelle - verranno caricati solo quando selezionati
databaseTables.Clear();
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel caricamento delle tabelle dal database connesso");
databaseErrorMessage = $"Errore nel caricamento tabelle: {ex.Message}";
throw;
}
}
/// <summary>
/// Carica la lista dei database disponibili dal server
/// </summary>
protected async Task LoadAvailableDatabases()
{
try
{
if (currentDatabaseManager == null)
{
databaseErrorMessage = "Database manager non disponibile";
return;
}
isLoadingDatabases = true;
Logger.LogInformation("Caricando database disponibili");
// Usa il metodo corretto dell'interfaccia IDatabaseManager
var allDatabases = await currentDatabaseManager.GetAvailableDatabasesAsync();
Logger.LogInformation("Ottenuti {DatabaseCount} database dal server", allDatabases.Count);
// Filtra i database di sistema
availableDatabases = FilterSystemDatabases(allDatabases).ToList();
Logger.LogInformation("Trovati {TotalDatabases} database, filtrati a {FilteredDatabases} (esclusi quelli di sistema)",
allDatabases.Count, availableDatabases.Count);
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel caricamento dei database disponibili");
databaseErrorMessage = $"Errore nel caricamento database: {ex.Message}";
}
finally
{
isLoadingDatabases = false;
}
}
/// <summary>
/// Tenta di auto-selezionare una chiave per le query custom
/// </summary>
protected void TryAutoSelectKeyForQuery(List<string> columns)
{
try
{
// Reset stato chiave
sourceKeyField = "";
suggestedPrimaryKey = "";
requiresManualKeySelection = true;
// Pattern comuni per identificare possibili chiavi primarie
var keyPatterns = new[]
{
"id", "ID", "Id",
"_id", "_ID", "_Id",
"key", "KEY", "Key",
"code", "CODE", "Code", "codice", "CODICE", "Codice",
"number", "NUMBER", "Number", "numero", "NUMERO", "Numero",
"index", "INDEX", "Index", "indice", "INDICE", "Indice"
};
// Cerca colonne che potrebbero essere chiavi primarie
string? detectedKey = null;
// 1. Cerca esattamente "id", "ID", "Id"
detectedKey = columns.FirstOrDefault(c =>
c.Equals("id", StringComparison.OrdinalIgnoreCase) ||
c.Equals("ID", StringComparison.Ordinal) ||
c.Equals("Id", StringComparison.Ordinal));
// 2. Se non trovato, cerca colonne che terminano con "id", "ID", "Id"
if (detectedKey == null)
{
detectedKey = columns.FirstOrDefault(c =>
c.EndsWith("id", StringComparison.OrdinalIgnoreCase) ||
c.EndsWith("ID", StringComparison.Ordinal) ||
c.EndsWith("Id", StringComparison.Ordinal));
}
// 3. Se non trovato, cerca colonne che contengono pattern di chiave
if (detectedKey == null)
{
foreach (var pattern in keyPatterns)
{
detectedKey = columns.FirstOrDefault(c =>
c.Contains(pattern, StringComparison.OrdinalIgnoreCase));
if (detectedKey != null) break;
}
}
// 4. Auto-seleziona se trovato
if (!string.IsNullOrEmpty(detectedKey))
{
sourceKeyField = detectedKey;
suggestedPrimaryKey = detectedKey;
requiresManualKeySelection = false;
Logger.LogInformation("Chiave auto-selezionata per query custom: {KeyField}", detectedKey);
}
else
{
Logger.LogInformation("Nessuna chiave rilevabile automaticamente per query custom, selezione manuale richiesta");
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nell'auto-selezione della chiave per query custom");
sourceKeyField = "";
suggestedPrimaryKey = "";
requiresManualKeySelection = true;
}
}
/// <summary>
/// Valida una custom query SQL
/// </summary>
protected async Task ValidateCustomQuery()
{
if (string.IsNullOrWhiteSpace(customQuery) || currentDatabaseManager == null)
{
isQueryValid = false;
queryValidationMessage = "Query vuota o manager database non disponibile";
return;
}
isValidatingQuery = true;
try
{
// Controllo di sicurezza: verifica che sia una SELECT
if (!IsSelectQuery(customQuery))
{
isQueryValid = false;
queryValidationMessage = "Solo query SELECT sono permesse per sicurezza";
return;
}
var cleanQuery = CleanQuery(customQuery);
// Trova la credenziale per determinare il tipo di database
var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential);
if (credential == null)
{
isQueryValid = false;
queryValidationMessage = "Credenziale database non trovata";
return;
}
// Crea una query di test con sintassi appropriata per il tipo di database
var testQuery = CreateLimitedQuery(cleanQuery, credential.DatabaseType, 1);
Logger.LogInformation("Validando query: {Query}", testQuery);
// Prova a eseguire la query per validarla
var testResults = await currentDatabaseManager.ExecuteRawQueryAsync(testQuery);
if (testResults != null && testResults.Any())
{
var firstRow = testResults.First();
queryColumns = firstRow.Keys.ToList();
isQueryValid = true;
queryValidationMessage = $"Query valida - {queryColumns.Count} colonne rilevate";
// Clear mappings quando cambia la query
ClearAllMappings();
// AUTO-SELECT della chiave per query custom
TryAutoSelectKeyForQuery(queryColumns);
Logger.LogInformation("Query validata con successo: {ColumnCount} colonne", queryColumns.Count);
}
else
{
isQueryValid = false;
queryValidationMessage = "La query non ha restituito risultati o colonne";
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nella validazione della query");
isQueryValid = false;
queryValidationMessage = $"Errore nella validazione: {ex.Message}";
}
finally
{
isValidatingQuery = false;
StateHasChanged();
}
}
/// <summary>
/// Carica un'anteprima dei dati della query
/// </summary>
protected async Task LoadQueryPreview()
{
if (!isQueryValid || string.IsNullOrWhiteSpace(customQuery) || currentDatabaseManager == null)
return;
isLoadingPreview = true;
try
{
var cleanQuery = CleanQuery(customQuery);
// Trova la credenziale per determinare il tipo di database
var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential);
if (credential == null)
{
queryValidationMessage = "Credenziale database non trovata";
return;
}
// Crea una query di anteprima con sintassi appropriata per il tipo di database
var previewQuery = CreateLimitedQuery(cleanQuery, credential.DatabaseType, 10);
Logger.LogInformation("Caricando anteprima con query: {Query}", previewQuery);
var previewResults = await currentDatabaseManager.ExecuteRawQueryAsync(previewQuery);
queryPreviewData = previewResults.ToList();
showQueryPreview = true;
Logger.LogInformation("Caricata anteprima query con {RecordCount} record", queryPreviewData.Count);
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel caricamento anteprima query");
queryValidationMessage = $"Errore anteprima: {ex.Message}";
}
finally
{
isLoadingPreview = false;
StateHasChanged();
}
}
/// <summary>
/// Crea una query limitata in base al tipo di database
/// </summary>
protected string CreateLimitedQuery(string baseQuery, DatabaseType databaseType, int limit)
{
return databaseType switch
{
DatabaseType.SqlServer => $"SELECT TOP {limit} * FROM ({baseQuery}) AS subquery",
DatabaseType.Oracle => $"SELECT * FROM ({baseQuery}) WHERE ROWNUM <= {limit}",
DatabaseType.MySql => $"{baseQuery} LIMIT {limit}",
DatabaseType.PostgreSql => $"{baseQuery} LIMIT {limit}",
DatabaseType.Sqlite => $"{baseQuery} LIMIT {limit}",
DatabaseType.DB2 => $"SELECT * FROM ({baseQuery}) FETCH FIRST {limit} ROWS ONLY",
DatabaseType.SapHana => $"{baseQuery} LIMIT {limit}",
_ => $"{baseQuery} LIMIT {limit}" // Default a LIMIT per database non riconosciuti
};
}
/// <summary>
/// Nasconde l'anteprima della query
/// </summary>
protected void HideQueryPreview()
{
showQueryPreview = false;
StateHasChanged();
}
/// <summary>
/// Tenta di caricare gli schemi con query diretta
/// </summary>
protected async Task TryLoadSchemasWithDirectQuery()
{
if (currentDatabaseManager == null)
return;
try
{
// Query diverse per ogni tipo di database - focalizzate sui database/cataloghi
var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential);
if (credential == null) return;
string? schemaQuery = credential.DatabaseType switch
{
DatabaseType.SqlServer => "SELECT name FROM sys.databases WHERE name NOT IN ('master', 'tempdb', 'model', 'msdb') AND state = 0",
DatabaseType.PostgreSql => "SELECT datname FROM pg_database WHERE datistemplate = false AND datname NOT IN ('postgres', 'template0', 'template1')",
DatabaseType.MySql => "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys')",
DatabaseType.Oracle => "SELECT DISTINCT OWNER FROM ALL_TABLES WHERE OWNER NOT IN ('SYS', 'SYSTEM', 'DBSNMP', 'SYSMAN', 'OUTLN', 'ANONYMOUS', 'CTXSYS', 'EXFSYS', 'LBACSYS', 'MDSYS', 'MGMT_VIEW', 'OLAPSYS', 'OWBSYS', 'ORDDATA', 'ORDSYS', 'SI_INFORMTN_SCHEMA', 'WK_TEST', 'WKPROXY', 'WMSYS', 'XDB', 'APEX_040000', 'APEX_PUBLIC_USER', 'DIP', 'FLOWS_FILES', 'HR', 'IX', 'OE', 'PM', 'SCOTT', 'SH', 'BI')",
_ => null
};
if (!string.IsNullOrEmpty(schemaQuery))
{
Logger.LogInformation("Eseguendo query per database/schemi: {Query}", schemaQuery);
var results = await currentDatabaseManager.ExecuteRawQueryAsync(schemaQuery);
if (results != null && results.Any())
{
var schemas = results.Select(row =>
{
var firstValue = row.Values.FirstOrDefault();
return firstValue?.ToString() ?? "";
})
.Where(schema => !string.IsNullOrEmpty(schema))
.OrderBy(schema => schema)
.ToList();
if (schemas.Any())
{
availableSchemas.AddRange(schemas);
Logger.LogInformation("Caricati {SchemaCount} database/schemi via query diretta per {DatabaseType}: {Schemas}",
schemas.Count, credential.DatabaseType, string.Join(", ", schemas));
}
}
}
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Errore nel caricamento database/schemi via query diretta");
}
}
/// <summary>
/// Gestisce la selezione database quando la discovery restituisce dizionario vuoto
/// </summary>
protected async Task HandleDatabaseSelectionRequired()
{
isLoadingDatabases = true;
showDatabaseSelectionModal = true;
availableDatabases.Clear();
selectedDatabase = "";
try
{
if (currentDatabaseManager != null)
{
var dbs = await currentDatabaseManager.GetAvailableDatabasesAsync();
availableDatabases = dbs ?? new List<string>();
}
}
catch (Exception ex)
{
databaseErrorMessage = $"Errore nel caricamento dei database: {ex.Message}";
}
finally
{
isLoadingDatabases = false;
}
}
/// <summary>
/// Gestisce la selezione di un database specifico
/// </summary>
protected async Task OnDatabaseSelected()
{
try
{
if (string.IsNullOrEmpty(selectedDatabase))
{
databaseErrorMessage = "Nessun database selezionato";
return;
}
showDatabaseSelectionModal = false;
Logger.LogInformation("Database selezionato: {DatabaseName}. Riconnessione in corso...", selectedDatabase);
// Riconnessione al database selezionato
await ConnectToDatabaseWithSpecificDatabase(selectedDatabase);
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nella selezione del database: {DatabaseName}", selectedDatabase);
databaseErrorMessage = $"Errore nella connessione al database {selectedDatabase}: {ex.Message}";
}
}
private IEnumerable<string> FilterSystemDatabases(List<string> allDatabases)
{
// Trova la credenziale per determinare il tipo di database
var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential);
if (credential == null)
{
Logger.LogWarning("Credenziale non trovata per filtraggio database di sistema");
return allDatabases; // Restituisce tutti se non riesce a determinare il tipo
}
var databaseType = credential.DatabaseType;
// Filtri per SQL Server
if (databaseType == DatabaseType.SqlServer)
{
var sqlServerSystemDatabases = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"master", "tempdb", "model", "msdb", "Resource", "mssqlsystemresource",
"ReportServer", "ReportServerTempDB", "SSISDB", "distribution"
};
return allDatabases.Where(db => !sqlServerSystemDatabases.Contains(db));
}
// TODO: Implementare filtri per altri tipi di database
if (databaseType == DatabaseType.MySql)
{
Logger.LogInformation("Filtro database di sistema MySQL - DA IMPLEMENTARE");
return allDatabases; // Per ora restituisce tutti
}
if (databaseType == DatabaseType.PostgreSql)
{
Logger.LogInformation("Filtro database di sistema PostgreSQL - DA IMPLEMENTARE");
return allDatabases; // Per ora restituisce tutti
}
if (databaseType == DatabaseType.Oracle)
{
Logger.LogInformation("Filtro database di sistema Oracle - DA IMPLEMENTARE");
return allDatabases; // Per ora restituisce tutti
}
Logger.LogWarning("Tipo database non riconosciuto per filtraggio: {DatabaseType}", databaseType);
return allDatabases; // Restituisce tutti per tipi non riconosciuti
}
private void CancelDatabaseSelection()
{
showDatabaseSelectionModal = false;
selectedDatabase = "";
databaseErrorMessage = "Selezione database annullata";
Logger.LogInformation("Selezione database annullata dall'utente");
}
// ===== FINE METODI DATABASE =====
}
@@ -0,0 +1,237 @@
using System;
using System.ComponentModel;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.JSInterop;
using Microsoft.Extensions.Logging;
using CredentialManager.Models;
using DataConnection.REST.Interfaces;
using DataConnection.REST.Models;
using DataConnection.CredentialManagement.Interfaces;
using DataConnection.Interfaces;
using Data_Coupler.Services;
using Data_Coupler.Models;
namespace Data_Coupler.Pages;
public partial class DataCoupler : ComponentBase
{
// ===== PROPRIETÀ REST =====
// Credenziali REST
protected List<RestApiCredential> restApiCredentials = new();
protected string selectedRestCredential = "";
// Stato connessioni REST
protected bool isConnectingRest = false;
protected bool isRestConnected = false;
protected string restErrorMessage = "";
// REST discovery
protected List<RestEntitySummary> restEntities = new();
protected RestEntitySummary? selectedRestEntity = null;
protected RestEntityInfo? restEntityDetails = null;
protected string restSearchTerm = "";
// Proprietà di mapping REST
protected string selectedRestProperty = "";
// Servizi REST
protected IRestMetadataDiscovery? currentRestDiscovery = null;
protected IRestServiceClient? currentRestClient = null;
// ===== METODI REST =====
/// <summary>
/// Carica le credenziali REST API
/// </summary>
protected async Task LoadRestCredentials()
{
try
{
restApiCredentials = await CredentialService.GetAllRestApiCredentialsAsync();
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel caricamento delle credenziali REST");
throw;
}
}
/// <summary>
/// Gestisce il cambio di credenziale REST
/// </summary>
private void OnRestCredentialChanged(ChangeEventArgs e)
{
var newCredential = e.Value?.ToString() ?? "";
// Clear the cache if we're switching to a different credential
if (!string.IsNullOrEmpty(selectedRestCredential) && selectedRestCredential != newCredential)
{
ConnectionFactory.ClearRestClientCache(selectedRestCredential);
Logger.LogInformation("Cleared REST client cache for credential: {CredentialName}", selectedRestCredential);
}
selectedRestCredential = newCredential;
ResetRestState();
}
/// <summary>
/// Resetta lo stato REST
/// </summary>
protected void ResetRestState()
{
isRestConnected = false;
restEntities.Clear();
selectedRestEntity = null;
restEntityDetails = null;
restSearchTerm = "";
restErrorMessage = "";
currentRestDiscovery = null;
currentRestClient = null;
// Clear mappings when resetting REST state - handled by main class
// ClearAllMappings();
}
/// <summary>
/// Connette al servizio REST API
/// </summary>
protected async Task ConnectToRestApi()
{
if (string.IsNullOrEmpty(selectedRestCredential))
return;
isConnectingRest = true;
restErrorMessage = "";
try
{
// Trova la credenziale
var credential = restApiCredentials.FirstOrDefault(c => c.Name == selectedRestCredential);
if (credential == null)
{
restErrorMessage = "Credenziale REST API non trovata";
return;
}
// Test della connessione
var (success, message) = await CredentialService.TestRestApiConnectionAsync(credential.Name);
if (!success)
{
restErrorMessage = $"Connessione fallita: {message}";
return;
}
// Crea i client REST usando il factory con le credenziali complete
currentRestClient = await ConnectionFactory.CreateRestServiceClientAsync(selectedRestCredential);
currentRestDiscovery = await ConnectionFactory.CreateRestMetadataDiscoveryAsync(selectedRestCredential);
Logger.LogInformation("Iniziando autenticazione per il servizio REST {ServiceType} con credenziale: {CredentialName}", credential.ServiceType, selectedRestCredential);
// Autenticazione prima del discovery
var authResult = await currentRestClient.AuthenticateAsync();
if (!authResult)
{
Logger.LogWarning("Autenticazione fallita per il servizio REST {ServiceType}", credential.ServiceType);
restErrorMessage = "Autenticazione fallita per il servizio REST";
return;
}
Logger.LogInformation("Autenticazione completata con successo per il servizio REST {ServiceType}", credential.ServiceType);
// Discovery delle entità disponibili
Logger.LogInformation("Iniziando discovery delle entità REST...");
var entities = await currentRestDiscovery.DiscoverEntitiesAsync();
restEntities = entities.Select(e => new RestEntitySummary
{
Name = e.Name,
Label = e.Name, // Use Name as Label since RestEntityInfo doesn't have Label
Description = "" // RestEntityInfo doesn't have Description
}).ToList();
isRestConnected = true;
Logger.LogInformation("Discovery completato: trovate {EntityCount} entità REST", restEntities.Count);
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nella connessione al servizio REST {ServiceType}", selectedRestCredential);
restErrorMessage = $"Errore nella connessione: {ex.Message}";
}
finally
{
isConnectingRest = false;
StateHasChanged();
}
}
/// <summary>
/// Seleziona un'entità REST
/// </summary>
protected async Task SelectRestEntity(RestEntitySummary entity)
{
selectedRestEntity = entity;
// Clear mappings when changing entity - handled by main class
// ClearAllMappings();
try
{
if (currentRestDiscovery != null)
{
// Discovery dei dettagli dell'entità
restEntityDetails = await currentRestDiscovery.DiscoverEntityDetailsAsync(entity.Name);
}
else
{
restErrorMessage = "Servizio di discovery REST non disponibile";
return;
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel caricamento dettagli entità {EntityName}", entity.Name);
restErrorMessage = $"Errore nel caricamento dettagli entità: {ex.Message}";
}
}
/// <summary>
/// Filtra le entità REST in base al termine di ricerca
/// </summary>
private IEnumerable<RestEntitySummary> GetFilteredRestEntities()
{
if (string.IsNullOrEmpty(restSearchTerm))
return restEntities;
return restEntities.Where(entity =>
entity.Name.Contains(restSearchTerm, StringComparison.OrdinalIgnoreCase) ||
(!string.IsNullOrEmpty(entity.Label) && entity.Label.Contains(restSearchTerm, StringComparison.OrdinalIgnoreCase)));
}
/// <summary>
/// Applica il filtro alle entità REST
/// </summary>
private async Task FilterRestEntities(ChangeEventArgs e)
{
restSearchTerm = e.Value?.ToString() ?? "";
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// Pulisce la ricerca delle entità REST
/// </summary>
private async Task ClearRestSearch()
{
restSearchTerm = "";
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// Seleziona una proprietà REST per il mapping
/// </summary>
protected void SelectRestProperty(string propertyName)
{
selectedRestProperty = propertyName;
}
}