Implementato sistema robusto di salvataggio/caricamento profili Data Coupler

- Aggiunto metodo GetCredentialIdByNameAsync in CredentialService per recuperare ID credenziali per nome
- Implementata gestione robusta dei profili duplicati con riattivazione, sovrascrittura e auto-rinomina
- Migliorata logica di caricamento profili con simulazione workflow utente e logging dettagliato
- Fixata gestione errori UNIQUE constraint nel salvataggio profili
- Aggiunto supporto per salvataggio ID credenziali reali invece di placeholder
- Implementato metodo GetProfileByNameIncludingInactiveAsync per gestire profili inattivi
- Aggiunto logging esteso per debug e troubleshooting
- Integrato componente ProfileSaver nella UI principale
- Risolti errori di compilazione e validazione build completa
- Migliorata gestione errori con feedback utente per credenziali/entità mancanti
This commit is contained in:
Alessio Dal Santo
2025-07-03 16:30:57 +02:00
parent d837339f7e
commit 65ed2bb93a
19 changed files with 967 additions and 115 deletions
+19 -17
View File
@@ -997,6 +997,25 @@
<i class="fas fa-list"></i> Riepilogo Mapping
</button>
}
</div>
<!-- Sezione Salvataggio Profilo -->
<div>
<ProfileSaver CanSave="@CanSaveProfile()"
SourceType="@selectedSourceType"
SourceCredentialId="@(GetCurrentSourceCredentialIdAsync().Result)"
SourceCredentialName="@selectedDatabaseCredential"
SourceSchema="@GetCurrentDatabaseSchema()"
SourceTable="@(useCustomQuery ? "custom_query" : selectedTable)"
SourceFilePath="@selectedFileName"
DestinationType="rest"
DestinationCredentialId="@(GetCurrentDestinationCredentialIdAsync().Result)"
DestinationCredentialName="@selectedRestCredential"
DestinationEndpoint="@selectedRestEntity?.Name"
FieldMappings="@GetCurrentFieldMappings()"
SourceKeyField="@sourceKeyField"
UseRecordAssociations="@useRecordAssociations"
OnProfileSaved="@OnProfileSaved" />
</div> <div class="text-muted">
@if (fieldMappings.Any())
{
@@ -1124,23 +1143,6 @@
}
</div>
<!-- Sezione Salvataggio Profilo -->
@if (isDatabaseConnected && isRestConnected && fieldMappings.Any())
{
<div class="row mt-4">
<div class="col-12">
<ProfileSaver CanSave="CanSaveProfile()"
SourceType="selectedSourceType"
SourceSchema="@(availableTableNames.FirstOrDefault()?.Split('.').FirstOrDefault())"
SourceTable="selectedTable"
DestinationType="rest"
DestinationEndpoint="@(selectedRestEntity?.Name)"
FieldMappings="GetCurrentFieldMappings()"
OnProfileSaved="OnProfileSaved" />
</div>
</div>
}
<!-- Componente Gestione Profili -->
<ProfileManagement ShowModal="showProfileManagement"
Profiles="availableProfiles"
+438 -90
View File
@@ -191,120 +191,427 @@ public partial class DataCoupler : ComponentBase
private async Task ApplyProfileConfiguration(DataCouplerProfile profile)
{
// Reset dello stato corrente
ResetAllState();
// Applica configurazione sorgente
selectedSourceType = profile.SourceType;
Logger.LogInformation("=== INIZIO APPLICAZIONE PROFILO ===");
Logger.LogInformation("Applicando configurazione profilo: {ProfileName}", profile.Name);
Logger.LogInformation("Profilo - SourceType: {SourceType}, SourceCredentialId: {SourceCredentialId}, DestinationCredentialId: {DestinationCredentialId}",
profile.SourceType, profile.SourceCredentialId, profile.DestinationCredentialId);
if (profile.SourceCredentialId.HasValue)
try
{
// Per ora, uso il nome della credenziale come identificatore
// TODO: Implementare risoluzione corretta tramite ID quando disponibile
// In alternativa, potremmo aggiungere il nome della credenziale al profilo
// Step 0: Log dello stato iniziale
Logger.LogInformation("Stato iniziale - SelectedSourceType: {SourceType}, DatabaseConnected: {DatabaseConnected}, RestConnected: {RestConnected}",
selectedSourceType, isDatabaseConnected, isRestConnected);
// Se c'è uno schema salvato nel profilo, utilizziamolo per la connessione
if (!string.IsNullOrEmpty(profile.SourceSchema))
{
Logger.LogInformation("Applicando schema dal profilo: {Schema}", profile.SourceSchema);
// Prima verifichiamo che ci sia una credenziale selezionata
if (!string.IsNullOrEmpty(selectedDatabaseCredential))
{
await ConnectToDatabaseWithSchema(profile.SourceSchema);
}
else
{
Logger.LogWarning("Nessuna credenziale database selezionata per applicare lo schema");
}
}
else if (!string.IsNullOrEmpty(selectedDatabaseCredential))
{
// Connetti al database senza schema specifico
await ConnectToDatabase();
}
// Reset dello stato corrente
Logger.LogInformation("Resettando stato corrente...");
ResetAllState();
Logger.LogInformation("Stato dopo reset - SelectedSourceType: {SourceType}, DatabaseConnected: {DatabaseConnected}, RestConnected: {RestConnected}",
selectedSourceType, isDatabaseConnected, isRestConnected);
// Seleziona la tabella se specificata
if (!string.IsNullOrEmpty(profile.SourceTable))
{
selectedTable = profile.SourceTable;
}
}
// Applica configurazione destinazione
if (profile.DestinationCredentialId.HasValue)
{
// Similmente, per ora gestiamo senza risoluzione diretta dell'ID
// TODO: Implementare risoluzione corretta tramite ID quando disponibile
// Step 1: Applica configurazione sorgente
selectedSourceType = profile.SourceType;
Logger.LogInformation("Step 1 - Tipo sorgente impostato: {SourceType}", selectedSourceType);
if (!string.IsNullOrEmpty(selectedRestCredential))
// Force UI update for source type change
StateHasChanged();
await Task.Delay(100); // Give UI time to react to source type change
// Step 2: Configura e connetti la sorgente
if (profile.SourceCredentialId.HasValue)
{
// Connetti al servizio REST
await ConnectToRestApi();
Logger.LogInformation("Step 2 - Configurazione sorgente con ID credenziale: {CredentialId}", profile.SourceCredentialId);
// Trova e seleziona l'entità REST
if (!string.IsNullOrEmpty(profile.DestinationEndpoint))
if (profile.SourceType == "database")
{
var entity = restEntities.FirstOrDefault(e => e.Name == profile.DestinationEndpoint);
if (entity != null)
var sourceCredential = await CredentialService.GetDatabaseCredentialAsync(profile.SourceCredentialId.Value);
if (sourceCredential != null)
{
await SelectRestEntity(entity);
selectedDatabaseCredential = sourceCredential.Name;
Logger.LogInformation("Credenziale database selezionata: {Credential}", selectedDatabaseCredential);
// Force UI update for credential selection
StateHasChanged();
await Task.Delay(200);
// Connetti al database
Logger.LogInformation("Iniziando connessione database...");
if (!string.IsNullOrEmpty(profile.SourceSchema))
{
Logger.LogInformation("Connessione con schema specifico: {Schema}", profile.SourceSchema);
await ConnectToDatabaseWithSchema(profile.SourceSchema);
}
else
{
Logger.LogInformation("Connessione senza schema specifico");
await ConnectToDatabase();
}
Logger.LogInformation("Stato dopo connessione database - Connected: {Connected}, Tables: {TableCount}",
isDatabaseConnected, availableTableNames.Count);
// Seleziona la tabella se specificata e se la connessione è riuscita
if (!string.IsNullOrEmpty(profile.SourceTable) && isDatabaseConnected)
{
Logger.LogInformation("Selezione tabella: {Table}", profile.SourceTable);
await SelectTable(profile.SourceTable);
Logger.LogInformation("Tabella selezionata: {SelectedTable}, Schema caricato: {SchemaLoaded}",
selectedTable, databaseTables.ContainsKey(profile.SourceTable));
}
else
{
Logger.LogWarning("Impossibile selezionare tabella - Table: {Table}, Connected: {Connected}",
profile.SourceTable, isDatabaseConnected);
}
}
else
{
Logger.LogWarning("Entità REST con endpoint {Endpoint} non trovata", profile.DestinationEndpoint);
Logger.LogWarning("Credenziale database con ID {CredentialId} non trovata", profile.SourceCredentialId);
}
}
}
}
// Applica mapping dei campi se disponibile
if (!string.IsNullOrEmpty(profile.FieldMappingJson))
{
try
{
var service = new DataCouplerProfileService(null!); // Temporaneo per deserializzazione
var mappings = service.DeserializeFieldMappings(profile.FieldMappingJson);
// Applica i mapping
fieldMappings.Clear();
keyFields.Clear();
foreach (var mapping in mappings)
else if (profile.SourceType == "file")
{
fieldMappings[mapping.SourceField] = mapping.DestinationField;
if (mapping.IsKey)
// Per i file, non possiamo ricreare il file caricato, ma possiamo impostare le informazioni
if (!string.IsNullOrEmpty(profile.SourceFilePath))
{
keyFields.Add(mapping.DestinationField);
selectedFileName = Path.GetFileName(profile.SourceFilePath);
Logger.LogInformation("Informazioni file impostate: {FileName}", selectedFileName);
}
}
Logger.LogInformation("Applicati {MappingCount} mapping dei campi dal profilo", mappings.Count);
}
catch (Exception ex)
else
{
Logger.LogWarning(ex, "Errore nel caricamento dei mapping dei campi dal profilo");
Logger.LogInformation("Nessuna credenziale sorgente da configurare");
}
// Small delay to let source configuration settle
await Task.Delay(300);
// Step 3: Configura e connetti la destinazione
if (profile.DestinationCredentialId.HasValue)
{
Logger.LogInformation("Step 3 - Configurazione destinazione con ID credenziale: {CredentialId}", profile.DestinationCredentialId);
var destinationCredential = await CredentialService.GetRestApiCredentialAsync(profile.DestinationCredentialId.Value);
if (destinationCredential != null)
{
selectedRestCredential = destinationCredential.Name;
Logger.LogInformation("Credenziale REST selezionata: {Credential}", selectedRestCredential);
// Force UI update for REST credential selection
StateHasChanged();
await Task.Delay(200);
// Connetti al servizio REST
Logger.LogInformation("Iniziando connessione REST...");
await ConnectToRestApi();
Logger.LogInformation("Stato dopo connessione REST - Connected: {Connected}, Entities: {EntityCount}",
isRestConnected, restEntities.Count);
// Seleziona l'entità REST se la connessione è riuscita
if (!string.IsNullOrEmpty(profile.DestinationEndpoint) && isRestConnected)
{
var entity = restEntities.FirstOrDefault(e => e.Name == profile.DestinationEndpoint);
if (entity != null)
{
Logger.LogInformation("Selezione entità REST: {Entity}", entity.Name);
await SelectRestEntity(entity);
Logger.LogInformation("Entità REST selezionata: {SelectedEntity}, Dettagli caricati: {DetailsLoaded}",
selectedRestEntity?.Name, restEntityDetails != null);
}
else
{
Logger.LogWarning("Entità REST non trovata: {Endpoint} - Entities disponibili: {Entities}",
profile.DestinationEndpoint, string.Join(", ", restEntities.Select(e => e.Name)));
}
}
else
{
Logger.LogWarning("Impossibile selezionare entità REST - Endpoint: {Endpoint}, Connected: {Connected}",
profile.DestinationEndpoint, isRestConnected);
}
}
else
{
Logger.LogWarning("Credenziale REST con ID {CredentialId} non trovata", profile.DestinationCredentialId);
}
}
else
{
Logger.LogInformation("Nessuna credenziale destinazione da configurare");
}
}
StateHasChanged();
// Small delay to let destination configuration settle
await Task.Delay(300);
// Step 4: Applica mapping dei campi se disponibile
if (!string.IsNullOrEmpty(profile.FieldMappingJson))
{
Logger.LogInformation("Step 4 - Applicazione mapping campi...");
try
{
var service = new DataCouplerProfileService(null!);
var mappings = service.DeserializeFieldMappings(profile.FieldMappingJson);
Logger.LogInformation("Mappings deserializzati: {Count}", mappings.Count);
// Applica i mapping
fieldMappings.Clear();
keyFields.Clear();
foreach (var mapping in mappings)
{
fieldMappings[mapping.SourceField] = mapping.DestinationField;
if (mapping.IsKey)
{
keyFields.Add(mapping.DestinationField);
}
Logger.LogInformation("Mapping applicato: {Source} -> {Destination} (IsKey: {IsKey})",
mapping.SourceField, mapping.DestinationField, mapping.IsKey);
}
Logger.LogInformation("Mappings applicati - Totale: {MappingCount}, Chiavi: {KeyCount}",
fieldMappings.Count, keyFields.Count);
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Errore nel caricamento dei mapping dei campi dal profilo");
}
}
else
{
Logger.LogInformation("Nessun mapping campi da applicare");
}
// Step 5: Applica configurazione chiave sorgente
if (!string.IsNullOrEmpty(profile.SourceKeyField))
{
sourceKeyField = profile.SourceKeyField;
Logger.LogInformation("Step 5 - Chiave sorgente applicata: {SourceKey}", sourceKeyField);
}
else
{
Logger.LogInformation("Nessuna chiave sorgente da applicare");
}
// Step 6: Applica configurazione associazioni record
useRecordAssociations = profile.UseRecordAssociations;
Logger.LogInformation("Step 6 - Associazioni record configurate: {UseAssociations}", useRecordAssociations);
Logger.LogInformation("=== FINE APPLICAZIONE PROFILO ===");
Logger.LogInformation("Stato finale - Source: {SourceType}, DatabaseConnected: {DatabaseConnected}, RestConnected: {RestConnected}, Mappings: {MappingCount}",
selectedSourceType, isDatabaseConnected, isRestConnected, fieldMappings.Count);
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nell'applicazione della configurazione del profilo {ProfileName}", profile.Name);
await JSRuntime.InvokeVoidAsync("alert", $"Errore nel caricamento del profilo: {ex.Message}");
}
finally
{
// Force final UI update
StateHasChanged();
Logger.LogInformation("Aggiornamento finale UI completato");
}
}
private async Task OnProfileSaved(DataCouplerProfileDto profileDto)
{
try
{
Logger.LogInformation("Tentativo di salvataggio profilo: {ProfileName}", profileDto.Name);
var profileService = new DataCouplerProfileService(null!); // Usa il service di conversione
var profile = profileService.FromDto(profileDto, "System"); // TODO: Usa utente corrente
await ProfileService.SaveProfileAsync(profile);
await LoadProfiles(); // Ricarica la lista
// Controlla se esiste già un profilo con lo stesso nome (inclusi quelli inattivi)
Logger.LogInformation("Controllo esistenza profilo con nome: {ProfileName}", profileDto.Name);
var existingProfile = await ProfileService.GetProfileByNameIncludingInactiveAsync(profileDto.Name);
await JSRuntime.InvokeVoidAsync("alert", $"Profilo '{profileDto.Name}' salvato con successo!");
if (existingProfile != null)
{
Logger.LogInformation("Trovato profilo esistente con ID: {ProfileId}, IsActive: {IsActive}",
existingProfile.Id, existingProfile.IsActive);
if (!existingProfile.IsActive)
{
// Il profilo esiste ma è inattivo - riattivalo e aggiornalo
Logger.LogInformation("Riattivazione del profilo inattivo: {ProfileName}", profileDto.Name);
profile.Id = existingProfile.Id;
profile.IsActive = true;
// Aggiorna direttamente il profilo esistente invece di creare un nuovo oggetto
existingProfile.Description = profile.Description;
existingProfile.SourceType = profile.SourceType;
existingProfile.SourceCredentialId = profile.SourceCredentialId;
existingProfile.SourceSchema = profile.SourceSchema;
existingProfile.SourceTable = profile.SourceTable;
existingProfile.SourceFilePath = profile.SourceFilePath;
existingProfile.DestinationType = profile.DestinationType;
existingProfile.DestinationCredentialId = profile.DestinationCredentialId;
existingProfile.DestinationSchema = profile.DestinationSchema;
existingProfile.DestinationTable = profile.DestinationTable;
existingProfile.DestinationEndpoint = profile.DestinationEndpoint;
existingProfile.FieldMappingJson = profile.FieldMappingJson;
existingProfile.SourceKeyField = profile.SourceKeyField;
existingProfile.UseRecordAssociations = profile.UseRecordAssociations;
existingProfile.IsActive = true;
await ProfileService.UpdateProfileAsync(existingProfile);
await LoadProfiles();
await JSRuntime.InvokeVoidAsync("alert", $"Profilo '{profileDto.Name}' riattivato e aggiornato con successo!");
return;
}
// Il profilo esiste ed è attivo - chiedi conferma per sovrascrittura
var confirmOverwrite = await JSRuntime.InvokeAsync<bool>("confirm",
$"Esiste già un profilo attivo con il nome '{profileDto.Name}'. Vuoi sovrascriverlo?");
if (confirmOverwrite)
{
Logger.LogInformation("Utente ha confermato la sovrascrittura del profilo: {ProfileName}", profileDto.Name);
// Aggiorna il profilo esistente direttamente
existingProfile.Description = profile.Description;
existingProfile.SourceType = profile.SourceType;
existingProfile.SourceCredentialId = profile.SourceCredentialId;
existingProfile.SourceSchema = profile.SourceSchema;
existingProfile.SourceTable = profile.SourceTable;
existingProfile.SourceFilePath = profile.SourceFilePath;
existingProfile.DestinationType = profile.DestinationType;
existingProfile.DestinationCredentialId = profile.DestinationCredentialId;
existingProfile.DestinationSchema = profile.DestinationSchema;
existingProfile.DestinationTable = profile.DestinationTable;
existingProfile.DestinationEndpoint = profile.DestinationEndpoint;
existingProfile.FieldMappingJson = profile.FieldMappingJson;
existingProfile.SourceKeyField = profile.SourceKeyField;
existingProfile.UseRecordAssociations = profile.UseRecordAssociations;
await ProfileService.UpdateProfileAsync(existingProfile);
await LoadProfiles(); // Ricarica la lista
await JSRuntime.InvokeVoidAsync("alert", $"Profilo '{profileDto.Name}' aggiornato con successo!");
}
else
{
Logger.LogInformation("Utente ha annullato la sovrascrittura del profilo: {ProfileName}", profileDto.Name);
// Proponi di creare con un nome unico
var useUniqueName = await JSRuntime.InvokeAsync<bool>("confirm",
"Vuoi salvare il profilo con un nome unico automatico (es. 'Nome Profilo (1)')?");
if (useUniqueName)
{
var uniqueName = await GenerateUniqueProfileName(profileDto.Name);
profile.Name = uniqueName;
try
{
await ProfileService.SaveProfileAsync(profile);
await LoadProfiles();
await JSRuntime.InvokeVoidAsync("alert", $"Profilo salvato con nome '{uniqueName}'!");
}
catch (Exception uniqueEx)
{
Logger.LogError(uniqueEx, "Errore durante il salvataggio del profilo con nome unico: {UniqueName}", uniqueName);
// Gestisci l'errore di unique constraint anche per il nome unico
if (uniqueEx.Message.Contains("UNIQUE constraint failed"))
{
await JSRuntime.InvokeVoidAsync("alert",
$"Errore: Non è stato possibile generare un nome unico per il profilo. " +
"Prova a ricaricare la pagina e riprova.");
}
else
{
await JSRuntime.InvokeVoidAsync("alert", $"Errore nel salvataggio del profilo: {uniqueEx.Message}");
}
}
}
// Altrimenti, non salvare nulla
return;
}
}
else
{
Logger.LogInformation("Nessun profilo esistente trovato, creazione nuovo profilo: {ProfileName}", profileDto.Name);
// Crea un nuovo profilo
try
{
await ProfileService.SaveProfileAsync(profile);
await LoadProfiles(); // Ricarica la lista
await JSRuntime.InvokeVoidAsync("alert", $"Profilo '{profileDto.Name}' salvato con successo!");
}
catch (Exception saveEx)
{
Logger.LogError(saveEx, "Errore durante il salvataggio del nuovo profilo: {ProfileName}", profileDto.Name);
// Possibile race condition - riprova con controllo duplicato
if (saveEx.Message.Contains("UNIQUE constraint failed"))
{
Logger.LogWarning("Race condition rilevata durante il salvataggio, gestione del duplicato...");
// Chiedi se vuole sovrascrivere o creare nome unico
var handleDuplicate = await JSRuntime.InvokeAsync<bool>("confirm",
$"Un profilo con il nome '{profileDto.Name}' è stato creato nel frattempo. " +
"Vuoi sovrascriverlo? (Clicca 'Annulla' per salvare con un nome unico)");
if (handleDuplicate)
{
// Trova il profilo e aggiornalo
var duplicateProfile = await ProfileService.GetProfileByNameIncludingInactiveAsync(profileDto.Name);
if (duplicateProfile != null)
{
profile.Id = duplicateProfile.Id;
await ProfileService.UpdateProfileAsync(profile);
await LoadProfiles();
await JSRuntime.InvokeVoidAsync("alert", $"Profilo '{profileDto.Name}' aggiornato con successo!");
}
else
{
await JSRuntime.InvokeVoidAsync("alert", "Errore: Il profilo duplicato non è stato trovato.");
}
}
else
{
// Crea con nome unico
var uniqueName = await GenerateUniqueProfileName(profileDto.Name);
profile.Name = uniqueName;
await ProfileService.SaveProfileAsync(profile);
await LoadProfiles();
await JSRuntime.InvokeVoidAsync("alert", $"Profilo salvato con nome '{uniqueName}'!");
}
}
else
{
throw; // Rilancia eccezioni non gestite
}
}
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel salvataggio del profilo");
await JSRuntime.InvokeVoidAsync("alert", $"Errore nel salvataggio del profilo: {ex.Message}");
Logger.LogError(ex, "Errore generale nel salvataggio del profilo: {ProfileName}", profileDto.Name);
// Gestione generica degli errori
if (ex.Message.Contains("UNIQUE constraint failed"))
{
await JSRuntime.InvokeVoidAsync("alert",
$"Errore: Esiste già un profilo con il nome '{profileDto.Name}'. " +
"Questo può accadere se ci sono stati problemi di sincronizzazione. " +
"Prova a ricaricare la pagina e riprova.");
}
else
{
await JSRuntime.InvokeVoidAsync("alert", $"Errore nel salvataggio del profilo: {ex.Message}");
}
}
}
@@ -930,7 +1237,7 @@ public partial class DataCoupler : ComponentBase
{
isConnectingRest = false;
}
} private async void SelectTable(string tableName)
} private async Task SelectTable(string tableName)
{
selectedTable = tableName;
@@ -1005,7 +1312,9 @@ public partial class DataCoupler : ComponentBase
}
StateHasChanged();
} private async Task SelectRestEntity(RestEntitySummary entity)
}
private async Task SelectRestEntity(RestEntitySummary entity)
{
selectedRestEntity = entity;
@@ -2015,6 +2324,7 @@ public partial class DataCoupler : ComponentBase
/// <summary>
/// Verifica se una query è una SELECT query sicura
/// </summary>
private bool IsSelectQuery(string query)
{
@@ -2262,20 +2572,44 @@ public partial class DataCoupler : ComponentBase
/// <summary>
/// Ottiene l'ID della credenziale sorgente corrente
/// </summary>
private int? GetCurrentSourceCredentialId()
private async Task<int?> GetCurrentSourceCredentialIdAsync()
{
// TODO: Implementare logica per ottenere l'ID dalla credenziale selezionata
// Per ora ritorniamo null dato che i DTO non hanno ID
if (selectedSourceType == "database" && !string.IsNullOrEmpty(selectedDatabaseCredential))
{
try
{
// Usa il nuovo metodo per ottenere direttamente l'ID della credenziale
return await CredentialService.GetCredentialIdByNameAsync(selectedDatabaseCredential, CredentialManager.Models.CredentialType.Database);
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nell'ottenere l'ID della credenziale database: {CredentialName}", selectedDatabaseCredential);
return null;
}
}
return null;
}
/// <summary>
/// Ottiene l'ID della credenziale destinazione corrente
/// </summary>
private int? GetCurrentDestinationCredentialId()
private async Task<int?> GetCurrentDestinationCredentialIdAsync()
{
// TODO: Implementare logica per ottenere l'ID dalla credenziale selezionata
// Per ora ritorniamo null dato che i DTO non hanno ID
if (!string.IsNullOrEmpty(selectedRestCredential))
{
try
{
// Usa il nuovo metodo per ottenere direttamente l'ID della credenziale
return await CredentialService.GetCredentialIdByNameAsync(selectedRestCredential, CredentialManager.Models.CredentialType.RestApi);
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nell'ottenere l'ID della credenziale REST: {CredentialName}", selectedRestCredential);
return null;
}
}
return null;
}
@@ -2400,7 +2734,7 @@ public partial class DataCoupler : ComponentBase
catch (Exception ex)
{
Logger.LogError(ex, "Errore nella connessione con lo schema selezionato");
databaseErrorMessage = $"Errore nella connessione con schema {selectedSchema}: {ex.Message}";
databaseErrorMessage = $"Errore nella connessione al database {selectedSchema}: {ex.Message}";
}
StateHasChanged();
@@ -2778,8 +3112,9 @@ public partial class DataCoupler : ComponentBase
"id", "ID", "Id",
"_id", "_ID", "_Id",
"key", "KEY", "Key",
"code", "CODE", "Code",
"number", "NUMBER", "Number"
"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
@@ -2912,5 +3247,18 @@ public partial class DataCoupler : ComponentBase
}
}
private async Task<string> GenerateUniqueProfileName(string baseName)
{
var uniqueName = baseName;
var counter = 1;
while (await ProfileService.GetProfileByNameIncludingInactiveAsync(uniqueName) != null)
{
uniqueName = $"{baseName} ({counter})";
counter++;
}
return uniqueName;
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.