Files
Alessio Dal Santo 344853fde9 [Feature/Perf] Ottimizzazioni bulk pre-discovery, batch deletion sync e supporto OLE DB / Salesforce client_credentials
## Bulk Pre-Discovery e riduzione query SQLite/SOQL

### KeyAssociationService — FindAssociationsByKeyValuesBulkAsync (nuovo)
- Aggiunta query bulk 'WHERE KeyValue IN (...)' per recuperare N associazioni con 1 sola query SQLite
  (chunking a 500 chiavi per rispettare il limite ~999 parametri di SQLite)
- Aggiunta interfaccia IKeyAssociationService e delegata in DataConnectionCredentialService / IDataConnectionCredentialService

### AssociationService — BatchFindOrCreateAssociationsAsync (nuovo)
- Nuovo metodo bulk che sostituisce i loop per-record durante l'analisi composite:
  1) 1 query SQLite bulk per tutte le chiavi
  2) Per le chiavi non trovate: SOQL 'IN (...)' su Salesforce in chunk da 200 via BatchExecuteQueriesAsync
     (ceil(K/25) HTTP Composite call invece di K singole)
  3) Salvataggio parallelo delle associazioni pre-discovery scoperte
- Fallback per-record automatico per client REST non Salesforce
- Aggiornata interfaccia IAssociationService con documentazione XML completa

### DataCoupler.razor.cs — STEP A/B nel flusso COMPOSITE
- Pre-Discovery spostata FUORI dal loop parallelo (STEP A, prima dell'analisi)
- associationsByKey pre-popolato con BatchFindOrCreateAssociationsAsync
- STEP B: il loop parallelo usa TryGetValue O(1) invece di query async per record
- Rimozione blocco ~40 righe di per-record lookup / fallback duplicati

## Salesforce Composite API — Batch Delete e Patch

### SalesforceServiceClient — metodi batch (nuovi)
- BatchDeleteEntitiesAsync: elimina N record con ceil(N/25) Composite call invece di N
- BatchPatchSingleFieldAsync: aggiorna un singolo campo su N record tramite BatchUpdateEntitiesAsync

### DeletionSyncService — refactoring batch
- ExecuteBatchedSalesforceDeletionsAsync: orchestrazione batch per Delete / Deactivate / Mark su Salesforce
- ExecuteSequentialDeletionsAsync: loop sequenziale esistente estratto in metodo riutilizzabile
- Dispatcher: Salesforce -> batch Composite, altri client REST -> sequenziale

## Supporto OLE DB (database)

### DatabaseSchemaProviderFactory
- Aggiunto case DatabaseType.OleDb -> new OleDbSchemaProvider() nel factory switch

### DatabaseMethod.cs
- Aggiunto metodo IsOleDbConnection() (parallelo a IsOdbcConnection())
- Query validation e manager temporaneo estesi a OLE DB oltre che ODBC
- GetLimitedQuery: aggiunto case OleDb -> 'SELECT TOP N FROM (subquery)'

## Salesforce OAuth2 — fix client_credentials

### CredentialService.cs
- Aggiunto 'GrantType' alla HashSet serviceSpecificKeys per preservarlo nella serializzazione AdditionalParameters

### DataConnectionCredentialService.cs
- Refactored BuildRestServiceOptions in helper statico riutilizzato da entrambi i metodi GetRestServiceOptions
- Mapping coerente ClientId/ClientSecret/GrantType per Salesforce (allineato a DataConnectionFactory)
- TestSalesforceOAuthLogin: branch esplicito per client_credentials (no username/password/token)
  con validazione preventiva ClientId+ClientSecret obbligatori
- Log flow label (password|client_credentials) in tutti i messaggi di autenticazione

## VS Code tasks

### .vscode/tasks.json
- Rimosso task generico 'Publish Data_Coupler'
- Aggiunti due task separati: win-x64 e win-x86, entrambi SingleFile + Self-Contained + ReadyToRun
2026-05-28 11:15:18 +02:00

106 lines
6.0 KiB
C#

using CredentialManager.Models;
using CredentialManager.Services;
namespace DataConnection.CredentialManagement.Interfaces;
/// <summary>
/// Interfaccia per la gestione delle credenziali integrate con DataConnection
/// </summary>
public interface IDataConnectionCredentialService
{
// Database credentials
Task<DatabaseCredential?> GetDatabaseCredentialAsync(string name);
Task<DatabaseCredential?> GetDatabaseCredentialAsync(int id);
Task<List<DatabaseCredential>> GetAllDatabaseCredentialsAsync();
Task<int> SaveDatabaseCredentialAsync(DatabaseCredential credential);
Task<bool> DeleteDatabaseCredentialAsync(int id);
Task<bool> DeleteDatabaseCredentialAsync(string name);
// REST API credentials
Task<RestApiCredential?> GetRestApiCredentialAsync(string name);
Task<RestApiCredential?> GetRestApiCredentialAsync(int id);
Task<List<RestApiCredential>> GetAllRestApiCredentialsAsync();
Task<int> SaveRestApiCredentialAsync(RestApiCredential credential);
Task<bool> DeleteRestApiCredentialAsync(int id);
Task<bool> DeleteRestApiCredentialAsync(string name);
// SAP B1 Service Layer credentials
Task<SapB1ServiceLayerCredential?> GetSapB1CredentialAsync(string name);
Task<SapB1ServiceLayerCredential?> GetSapB1CredentialAsync(int id);
Task<List<SapB1ServiceLayerCredential>> GetAllSapB1CredentialsAsync();
Task<int> SaveSapB1CredentialAsync(SapB1ServiceLayerCredential credential);
Task<bool> DeleteSapB1CredentialAsync(int id);
Task<bool> DeleteSapB1CredentialAsync(string name);
// Salesforce credentials
Task<SalesforceCredential?> GetSalesforceCredentialAsync(string name);
Task<SalesforceCredential?> GetSalesforceCredentialAsync(int id);
Task<List<SalesforceCredential>> GetAllSalesforceCredentialsAsync();
Task<int> SaveSalesforceCredentialAsync(SalesforceCredential credential);
Task<bool> DeleteSalesforceCredentialAsync(int id);
Task<bool> DeleteSalesforceCredentialAsync(string name);
// DataConnection specific operations
Task<string> GetConnectionStringAsync(string credentialName);
Task<string> GetConnectionStringAsync(int credentialId);
Task<DataConnection.EF.DbManagerOptions> GetDbManagerOptionsAsync(string credentialName);
Task<DataConnection.EF.DbManagerOptions> GetDbManagerOptionsAsync(int credentialId);
Task<DataConnection.REST.Configuration.RestServiceOptions> GetRestServiceOptionsAsync(string credentialName);
Task<DataConnection.REST.Configuration.RestServiceOptions> GetRestServiceOptionsAsync(int credentialId);
// Helper methods
Task<int?> GetCredentialIdByNameAsync(string name, CredentialManager.Models.CredentialType type);
// Connection testing
Task<(bool Success, string Message)> TestDatabaseConnectionAsync(string credentialName);
Task<(bool Success, string Message)> TestDatabaseConnectionAsync(DatabaseCredential credential);
Task<(bool Success, string Message)> TestRestApiConnectionAsync(string credentialName);
Task<(bool Success, string Message)> TestRestApiConnectionAsync(RestApiCredential credential);
Task<(bool Success, string Message)> TestSapB1ConnectionAsync(string credentialName);
Task<(bool Success, string Message)> TestSapB1ConnectionAsync(SapB1ServiceLayerCredential credential);
Task<(bool Success, string Message)> TestSalesforceConnectionAsync(string credentialName);
Task<(bool Success, string Message)> TestSalesforceConnectionAsync(SalesforceCredential credential);
// Key associations
Task<int> SaveKeyAssociationAsync(KeyAssociation association);
Task<int> SaveKeyAssociationParallelAsync(KeyAssociation association);
Task<KeyAssociation?> FindKeyAssociationByValueAsync(string keyValue, string destinationEntity, string restCredentialName);
Task<KeyAssociation?> FindKeyAssociationByValueAsync(string keyValue);
Task<List<KeyAssociation>> GetKeyAssociationsByDestinationAsync(string destinationEntity, string restCredentialName);
Task<List<KeyAssociation>> GetAllActiveKeyAssociationsAsync();
Task<List<KeyAssociation>> GetAllKeyAssociationsAsync();
Task<bool> UpdateKeyAssociationAsync(KeyAssociation association);
Task<bool> DeactivateKeyAssociationAsync(int id);
Task<bool> DeleteKeyAssociationAsync(int id);
Task<int> ClearKeyAssociationsAsync(string destinationEntity, string restCredentialName);
Task<int> ClearAllKeyAssociationsAsync();
Task<List<KeyAssociation>> GetInvalidKeyAssociationsAsync(string destinationEntity, string restCredentialName);
Task<int> CleanupInvalidKeyAssociationsAsync(string destinationEntity, string restCredentialName);
Task<bool> UpdateKeyAssociationLastVerifiedAsync(int id);
Task<AssociationStatistics> GetKeyAssociationStatisticsAsync();
// Parallel key association operations
Task<bool> SaveKeyAssociationParallelAsync(string keyValue, string destinationEntity, string destinationId, string restCredentialName);
Task<KeyAssociation?> FindKeyAssociationByValueParallelAsync(string keyValue, string destinationEntity, string restCredentialName);
Task<KeyAssociation?> FindKeyAssociationByValueParallelAsync(string keyValue);
Task<bool> DeleteKeyAssociationParallelAsync(int id);
/// <summary>
/// Bulk lookup associazioni - una sola query SQLite per N chiavi.
/// </summary>
Task<Dictionary<string, KeyAssociation>> FindKeyAssociationsByValuesBulkAsync(
IEnumerable<string> keyValues,
string destinationEntity,
string restCredentialName);
// Deletion synchronization operations
Task<int> MarkDeletedAssociationsAsync(List<string> sourceKeyValues, string destinationEntity, string restCredentialName);
Task<List<KeyAssociation>> GetPendingDeletionsAsync(string destinationEntity, string restCredentialName);
Task<bool> MarkDeletionSyncedAsync(int associationId);
Task<List<KeyAssociation>> GetDeletedAssociationsAsync(string destinationEntity, string restCredentialName);
// Cascade delete operations
Task<bool> DeleteCredentialCascadeAsync(string name);
Task<bool> DeleteCredentialCascadeAsync(int id);
}