Files
Data-Coupler/Components/ProfileSaver.razor.cs
T
Alessio 483eb7b407 Fix: Risolto double-mapping negli External ID Relationships per Salesforce
- Implementata funzionalità completa External ID Relationships nell'interfaccia di mapping
- Corretto bug double-mapping: i campi sorgente usati per External ID non vengono più inclusi nei mapping normali
- Risolto errore MALFORMED_ID causato dall'invio duplicato di campi come proprietà dirette e nested objects
- Implementata logica corretta per relationship names: oggetti standard usano il nome diretto, custom objects usano suffisso __r
- Aggiunta UI a 3 colonne (Object, External ID Field, Source Field) per configurazione External ID Relationships
- Migrazione database per supporto External ID Relationships nei profili
- Aggiornato ProfileSaver.razor.cs per salvare/caricare External ID Relationships
- Aggiornato ScheduledProfileExecutionService.cs per gestire External ID nelle esecuzioni schedulate
- Formato JSON output corretto: { 'Account': { 'CardCode__c': 'V50000' } }

Documentazione: EXTERNAL_ID_RELATIONSHIPS_IMPLEMENTATION.md
2026-02-15 18:44:15 +01:00

172 lines
6.0 KiB
C#

using Microsoft.AspNetCore.Components;
using CredentialManager.Models;
using CredentialManager.Services;
using System.ComponentModel.DataAnnotations;
namespace Components;
public partial class ProfileSaver
{
[Inject] private ICredentialService CredentialService { get; set; } = default!;
[Parameter] public bool CanSave { get; set; }
[Parameter] public string SourceType { get; set; } = "";
[Parameter] public int? SourceCredentialId { get; set; }
[Parameter] public string? SourceCredentialName { get; set; }
[Parameter] public string? SourceDatabaseName { get; set; }
[Parameter] public string? SourceSchema { get; set; }
[Parameter] public string? SourceTable { get; set; }
[Parameter] public string? SourceCustomQuery { get; set; }
[Parameter] public string? SourceFilePath { get; set; }
[Parameter] public string DestinationType { get; set; } = "";
[Parameter] public int? DestinationCredentialId { get; set; }
[Parameter] public string? DestinationCredentialName { get; set; }
[Parameter] public string? DestinationSchema { get; set; }
[Parameter] public string? DestinationTable { get; set; }
[Parameter] public string? DestinationEndpoint { get; set; }
[Parameter] public List<FieldMappingDto>? FieldMappings { get; set; }
[Parameter] public List<ExternalIdRelationshipDto>? ExternalIdRelationships { get; set; }
[Parameter] public string? SourceKeyField { get; set; }
[Parameter] public bool UseRecordAssociations { get; set; }
[Parameter] public EventCallback<DataCouplerProfileDto> OnProfileSaved { get; set; }
private bool ShowSaveForm { get; set; } = false;
private bool IsSaving { get; set; } = false;
private string SaveMessage { get; set; } = "";
private string SaveMessageType { get; set; } = "info";
private ProfileFormModel ProfileData { get; set; } = new();
private void ShowSaveDialog()
{
ProfileData = new ProfileFormModel();
ShowSaveForm = true;
SaveMessage = "";
}
private void CancelSave()
{
ShowSaveForm = false;
SaveMessage = "";
ProfileData = new();
}
private async Task SaveProfile()
{
IsSaving = true;
SaveMessage = "";
try
{
// Recupera automaticamente il nome del database dalla connessione attiva
var sourceDatabaseName = await GetSourceDatabaseNameAsync();
var profileDto = new DataCouplerProfileDto
{
Name = ProfileData.Name,
Description = ProfileData.Description,
SourceType = SourceType,
SourceCredentialId = SourceCredentialId,
SourceCredentialName = SourceCredentialName,
SourceDatabaseName = sourceDatabaseName,
SourceSchema = SourceSchema,
SourceTable = SourceTable,
SourceCustomQuery = SourceCustomQuery,
SourceFilePath = SourceFilePath,
DestinationType = DestinationType,
DestinationCredentialId = DestinationCredentialId,
DestinationCredentialName = DestinationCredentialName,
DestinationSchema = DestinationSchema,
DestinationTable = DestinationTable,
DestinationEndpoint = DestinationEndpoint,
FieldMappings = FieldMappings,
ExternalIdRelationships = ExternalIdRelationships,
SourceKeyField = SourceKeyField,
UseRecordAssociations = UseRecordAssociations
};
await OnProfileSaved.InvokeAsync(profileDto);
SaveMessage = $"Profilo '{ProfileData.Name}' salvato con successo!";
SaveMessageType = "success";
// Reset form after successful save
await Task.Delay(1500); // Show success message briefly
ShowSaveForm = false;
ProfileData = new();
}
catch (Exception ex)
{
SaveMessage = $"Errore nel salvataggio: {ex.Message}";
SaveMessageType = "danger";
}
finally
{
IsSaving = false;
}
}
private string GetSourceSummary()
{
return SourceType switch
{
"database" => "Database",
"file" => "File Excel/CSV",
_ => "Non configurato"
};
}
private string GetDestinationSummary()
{
return DestinationType switch
{
"database" => "Database",
"rest" => "REST API",
_ => "Non configurato"
};
}
public void SetMessage(string message, string type = "info")
{
SaveMessage = message;
SaveMessageType = type;
}
private async Task<string?> GetSourceDatabaseNameAsync()
{
// Prima priorità: se SourceDatabaseName è già impostato come parametro, usa quello
if (!string.IsNullOrEmpty(SourceDatabaseName))
{
return SourceDatabaseName;
}
// Seconda priorità: se abbiamo un SourceCredentialId, recupera il database dalle credenziali
if (SourceCredentialId.HasValue)
{
try
{
var credential = await CredentialService.GetDatabaseCredentialAsync(SourceCredentialId.Value);
if (credential != null && !string.IsNullOrEmpty(credential.DatabaseName))
{
return credential.DatabaseName;
}
}
catch (Exception)
{
// Se non riesce a recuperare le credenziali, continua con null
}
}
return null;
}
public class ProfileFormModel
{
[Required(ErrorMessage = "Il nome del profilo è obbligatorio")]
[StringLength(100, ErrorMessage = "Il nome non può superare i 100 caratteri")]
public string Name { get; set; } = "";
[StringLength(500, ErrorMessage = "La descrizione non può superare i 500 caratteri")]
public string? Description { get; set; }
}
}