[Feature] Salesforce: batch describe metadati, discovery parallela e fix scheduler External ID
- Salesforce Composite Batch API per describe SObject: le describe sono ora raggruppate in chunk da 25 e inviate come singole POST a /composite/batch, riducendo le chiamate API da N a ceil(N/25); per 200 SObject: da 201 a 9 chiamate. - Discovery entita' REST in parallelo: DiscoverEntitySummariesAsync e DiscoverEntitiesAsync avviate simultaneamente; la lista entita' diventa interattiva subito dopo le summaries, i dettagli completano in background con StateHasChanged() per aggiornare l'UI istantaneamente. - Fix scheduler - preservazione ExternalIdRelationshipsJson e DefaultValuesJson: in DataCoupler.razor.cs entrambi i blocchi di update profilo esistente (riattivazione profilo inattivo e sovrascrittura profilo attivo) omettevano questi campi nella copia, causandone l'azzeramento silenzioso ad ogni re-salvataggio. Ora entrambi i percorsi propagano correttamente i campi JSON. - Fix scheduler - esclusione campi sorgente External ID dal mapping normale: in ScheduledProfileExecutionService.TransformRecordForRest i campi sorgente usati nelle External ID Relationships venivano inclusi anche nel loop di field mapping standard, generando dati duplicati nell'entita' destinazione. Ora il comportamento e' allineato alla UI manuale (TransformRecordToRestEntity). - Aggiornata documentazione: README.md, AGENTS.md, copilot-instructions.md
This commit was merged in pull request #11.
This commit is contained in:
@@ -441,8 +441,26 @@ public partial class DataCoupler : ComponentBase
|
||||
if (relationships != null && relationships.Any())
|
||||
{
|
||||
externalIdRelationships.Clear();
|
||||
externalIdRelationships.AddRange(relationships);
|
||||
Logger.LogInformation("External ID Relationships caricate - Totale: {Count}", externalIdRelationships.Count);
|
||||
|
||||
// Normalizza i RelationshipName in base al tipo di oggetto destinazione
|
||||
bool isDestinationCustom = selectedRestEntity?.Name?.EndsWith("__c") ?? false;
|
||||
|
||||
foreach (var rel in relationships)
|
||||
{
|
||||
// Normalizza il RelationshipName
|
||||
string normalizedName = NormalizeRelationshipName(rel.RelatedObjectName, isDestinationCustom);
|
||||
|
||||
if (normalizedName != rel.RelationshipName)
|
||||
{
|
||||
Logger.LogInformation("Normalizzato RelationshipName: {Old} → {New} (Destination: {Destination}, IsCustom: {IsCustom})",
|
||||
rel.RelationshipName, normalizedName, selectedRestEntity?.Name, isDestinationCustom);
|
||||
rel.RelationshipName = normalizedName;
|
||||
}
|
||||
|
||||
externalIdRelationships.Add(rel);
|
||||
}
|
||||
|
||||
Logger.LogInformation("External ID Relationships caricate e normalizzate - Totale: {Count}", externalIdRelationships.Count);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -546,6 +564,8 @@ public partial class DataCoupler : ComponentBase
|
||||
existingProfile.DestinationTable = profile.DestinationTable;
|
||||
existingProfile.DestinationEndpoint = profile.DestinationEndpoint;
|
||||
existingProfile.FieldMappingJson = profile.FieldMappingJson;
|
||||
existingProfile.ExternalIdRelationshipsJson = profile.ExternalIdRelationshipsJson;
|
||||
existingProfile.DefaultValuesJson = profile.DefaultValuesJson;
|
||||
existingProfile.SourceKeyField = profile.SourceKeyField;
|
||||
existingProfile.UseRecordAssociations = profile.UseRecordAssociations;
|
||||
existingProfile.IsActive = true;
|
||||
@@ -579,6 +599,8 @@ public partial class DataCoupler : ComponentBase
|
||||
existingProfile.DestinationTable = profile.DestinationTable;
|
||||
existingProfile.DestinationEndpoint = profile.DestinationEndpoint;
|
||||
existingProfile.FieldMappingJson = profile.FieldMappingJson;
|
||||
existingProfile.ExternalIdRelationshipsJson = profile.ExternalIdRelationshipsJson;
|
||||
existingProfile.DefaultValuesJson = profile.DefaultValuesJson;
|
||||
existingProfile.SourceKeyField = profile.SourceKeyField;
|
||||
existingProfile.UseRecordAssociations = profile.UseRecordAssociations;
|
||||
|
||||
@@ -1550,20 +1572,13 @@ public partial class DataCoupler : ComponentBase
|
||||
return;
|
||||
}
|
||||
|
||||
// Determina il nome della relazione in base al tipo di oggetto
|
||||
// Salesforce: oggetti STANDARD usano solo il nome (es. "Account")
|
||||
// oggetti CUSTOM (finiscono con __c) usano __r (es. "CustomObject__r")
|
||||
string relationshipName;
|
||||
if (selectedRelationshipObject.EndsWith("__c"))
|
||||
{
|
||||
// Oggetto custom: rimuovi __c e aggiungi __r
|
||||
relationshipName = selectedRelationshipObject.Replace("__c", "__r");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Oggetto standard: usa solo il nome
|
||||
relationshipName = selectedRelationshipObject;
|
||||
}
|
||||
// Determina il nome della relazione usando il metodo helper
|
||||
bool isDestinationCustom = selectedRestEntity?.Name?.EndsWith("__c") ?? false;
|
||||
string relationshipName = NormalizeRelationshipName(selectedRelationshipObject, isDestinationCustom);
|
||||
|
||||
Logger.LogDebug("Creazione relazione - Destinazione: {Destination} (Custom: {IsCustom}), Correlato: {Related}, RelationshipName: {RelationshipName}",
|
||||
selectedRestEntity?.Name, isDestinationCustom, selectedRelationshipObject, relationshipName);
|
||||
|
||||
|
||||
// Crea la relazione
|
||||
var relationship = new ExternalIdRelationshipDto
|
||||
@@ -1606,6 +1621,47 @@ public partial class DataCoupler : ComponentBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizza il nome della relazione in base al tipo di oggetto destinazione.
|
||||
/// Salesforce External ID Relationships:
|
||||
/// - Se l'oggetto DESTINAZIONE è CUSTOM → usa sempre __r per tutte le relazioni
|
||||
/// - Se l'oggetto DESTINAZIONE è STANDARD → usa __r solo per oggetti custom correlati
|
||||
/// </summary>
|
||||
/// <param name="relatedObjectName">Nome dell'oggetto correlato (es. "Account", "Custom_Company__c")</param>
|
||||
/// <param name="isDestinationCustom">True se l'oggetto destinazione è custom</param>
|
||||
/// <returns>Nome normalizzato della relazione (es. "Account__r", "Account", "Custom_Company__r")</returns>
|
||||
private string NormalizeRelationshipName(string relatedObjectName, bool isDestinationCustom)
|
||||
{
|
||||
if (isDestinationCustom)
|
||||
{
|
||||
// Destinazione CUSTOM: tutte le relazioni usano __r
|
||||
if (relatedObjectName.EndsWith("__c"))
|
||||
{
|
||||
// Oggetto correlato custom: rimuovi __c e aggiungi __r
|
||||
return relatedObjectName.Replace("__c", "__r");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Oggetto correlato standard: aggiungi __r
|
||||
return relatedObjectName + "__r";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Destinazione STANDARD: solo oggetti custom correlati usano __r
|
||||
if (relatedObjectName.EndsWith("__c"))
|
||||
{
|
||||
// Oggetto correlato custom: rimuovi __c e aggiungi __r
|
||||
return relatedObjectName.Replace("__c", "__r");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Oggetto correlato standard: usa solo il nome
|
||||
return relatedObjectName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> GetExternalIdFieldsForSelectedObject()
|
||||
{
|
||||
if (string.IsNullOrEmpty(selectedRelationshipObject))
|
||||
|
||||
Reference in New Issue
Block a user