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:
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Data_Coupler.Models;
|
||||||
|
|
||||||
|
// Classe per i risultati del trasferimento
|
||||||
|
public class TransferResult
|
||||||
|
{
|
||||||
|
public int RecordNumber { get; set; }
|
||||||
|
public string Status { get; set; } = ""; // "success", "error", "updated", "duplicate"
|
||||||
|
public string Message { get; set; } = "";
|
||||||
|
public string? EntityId { get; set; }
|
||||||
|
public Dictionary<string, object> RecordData { get; set; } = new();
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user