483eb7b407
- 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
417 lines
15 KiB
C#
417 lines
15 KiB
C#
namespace CredentialManager.Models;
|
|
|
|
/// <summary>
|
|
/// Tipi di credenziali supportate
|
|
/// </summary>
|
|
public enum CredentialType
|
|
{
|
|
Database,
|
|
RestApi,
|
|
OAuth,
|
|
ApiKey,
|
|
BasicAuth
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tipi di servizi REST specifici
|
|
/// </summary>
|
|
public enum RestServiceType
|
|
{
|
|
Generic,
|
|
SapB1ServiceLayer,
|
|
Salesforce
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tipi di database supportati (allineato con DataConnection.Enums.DatabaseType)
|
|
/// </summary>
|
|
public enum DatabaseType
|
|
{
|
|
SqlServer,
|
|
MySql,
|
|
PostgreSql,
|
|
Oracle,
|
|
Sqlite,
|
|
DB2,
|
|
SapHana,
|
|
Odbc
|
|
}
|
|
|
|
/// <summary>
|
|
/// Modalità di connessione ODBC
|
|
/// </summary>
|
|
public enum OdbcConnectionMode
|
|
{
|
|
/// <summary>
|
|
/// Utilizzo di un DSN (Data Source Name) configurato
|
|
/// </summary>
|
|
Dsn,
|
|
|
|
/// <summary>
|
|
/// Costruzione manuale della connection string
|
|
/// </summary>
|
|
Custom
|
|
}
|
|
|
|
/// <summary>
|
|
/// DTO per le credenziali database - completo per tutti i tipi di DB supportati
|
|
/// </summary>
|
|
public class DatabaseCredential
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public DatabaseType DatabaseType { get; set; }
|
|
public string Host { get; set; } = string.Empty;
|
|
public int Port { get; set; }
|
|
public string? DatabaseName { get; set; } = string.Empty; // Ora opzionale
|
|
public string Username { get; set; } = string.Empty;
|
|
public string Password { get; set; } = string.Empty;
|
|
public string? ConnectionString { get; set; }
|
|
public int CommandTimeout { get; set; } = 30;
|
|
public bool IgnoreSslErrors { get; set; } = false;
|
|
public Dictionary<string, string>? AdditionalParameters { get; set; }
|
|
|
|
// ODBC specific properties
|
|
public string? OdbcDsnName { get; set; } // Nome del DSN ODBC (se utilizzato)
|
|
public OdbcConnectionMode OdbcMode { get; set; } = OdbcConnectionMode.Dsn; // Modalità ODBC (DSN o Custom)
|
|
}
|
|
|
|
/// <summary>
|
|
/// DTO per le credenziali REST API - allineato con RestServiceOptions e esteso per servizi specifici
|
|
/// </summary>
|
|
public class RestApiCredential
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public RestServiceType ServiceType { get; set; } = RestServiceType.Generic;
|
|
public string BaseUrl { get; set; } = string.Empty;
|
|
public string? ApiKey { get; set; }
|
|
public string? Username { get; set; }
|
|
public string? Password { get; set; }
|
|
public string? AuthToken { get; set; }
|
|
public string? BearerToken { get; set; }
|
|
public int TimeoutSeconds { get; set; } = 100;
|
|
public bool IgnoreSslErrors { get; set; } = false;
|
|
public Dictionary<string, string>? Headers { get; set; }
|
|
public Dictionary<string, string>? AdditionalParameters { get; set; }
|
|
|
|
// Campi specifici per SAP B1 Service Layer
|
|
public string? CompanyDatabase { get; set; }
|
|
public string? Language { get; set; } = "en-US";
|
|
public string? Version { get; set; } = "v1";
|
|
public bool UseTrustedConnection { get; set; } = false;
|
|
|
|
// Campi specifici per Salesforce
|
|
public string? SecurityToken { get; set; }
|
|
public string? ClientId { get; set; }
|
|
public string? ClientSecret { get; set; }
|
|
public string? ApiVersion { get; set; } = "59.0";
|
|
public bool IsSandbox { get; set; } = false;
|
|
public bool UseSoapApi { get; set; } = false;
|
|
public string? RefreshToken { get; set; }
|
|
public string? AccessToken { get; set; }
|
|
public DateTime? TokenExpiry { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Credenziali specifiche per SAP Business One Service Layer
|
|
/// </summary>
|
|
public class SapB1ServiceLayerCredential
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string ServerUrl { get; set; } = string.Empty; // es: https://server:50000/b1s/v1/
|
|
public string CompanyDatabase { get; set; } = string.Empty; // Database dell'azienda SAP
|
|
public string Username { get; set; } = string.Empty;
|
|
public string Password { get; set; } = string.Empty;
|
|
public string? Language { get; set; } = "en-US"; // Lingua per la sessione
|
|
public int TimeoutSeconds { get; set; } = 300; // Timeout per le chiamate API
|
|
public bool IgnoreSslErrors { get; set; } = false;
|
|
public string? Version { get; set; } = "v1"; // Versione del Service Layer (v1, v2, etc.)
|
|
public bool UseTrustedConnection { get; set; } = false; // Per autenticazione Windows
|
|
public Dictionary<string, string>? AdditionalHeaders { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Credenziali specifiche per Salesforce
|
|
/// </summary>
|
|
public class SalesforceCredential
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string LoginUrl { get; set; } = "https://login.salesforce.com"; // o https://test.salesforce.com per sandbox
|
|
public string Username { get; set; } = string.Empty;
|
|
public string Password { get; set; } = string.Empty;
|
|
public string SecurityToken { get; set; } = string.Empty; // Token di sicurezza Salesforce
|
|
public string? ClientId { get; set; } = string.Empty; // Consumer Key per Connected App
|
|
public string? ClientSecret { get; set; } = string.Empty; // Consumer Secret per Connected App
|
|
public string ApiVersion { get; set; } = "59.0"; // Versione API Salesforce
|
|
public bool IsSandbox { get; set; } = false; // Se è un ambiente sandbox
|
|
public int TimeoutSeconds { get; set; } = 120;
|
|
public bool UseSoapApi { get; set; } = false; // Se usare SOAP invece di REST
|
|
public string? RefreshToken { get; set; }
|
|
public string? AccessToken { get; set; }
|
|
public DateTime? TokenExpiry { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Factory per creare connection string per i diversi tipi di database
|
|
/// </summary>
|
|
public static class ConnectionStringBuilder
|
|
{
|
|
public static string BuildConnectionString(DatabaseCredential credential)
|
|
{
|
|
if (!string.IsNullOrEmpty(credential.ConnectionString))
|
|
return credential.ConnectionString;
|
|
|
|
return credential.DatabaseType switch
|
|
{
|
|
DatabaseType.SqlServer => BuildSqlServerConnectionString(credential),
|
|
DatabaseType.MySql => BuildMySqlConnectionString(credential),
|
|
DatabaseType.PostgreSql => BuildPostgreSqlConnectionString(credential),
|
|
DatabaseType.Oracle => BuildOracleConnectionString(credential),
|
|
DatabaseType.Sqlite => BuildSqliteConnectionString(credential),
|
|
DatabaseType.DB2 => BuildDb2ConnectionString(credential),
|
|
DatabaseType.SapHana => BuildSapHanaConnectionString(credential),
|
|
DatabaseType.Odbc => BuildOdbcConnectionString(credential),
|
|
_ => throw new NotSupportedException($"Database type {credential.DatabaseType} not supported")
|
|
};
|
|
} private static string BuildSqlServerConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>();
|
|
|
|
// Gestione speciale per SQL Server locale e named instances
|
|
// Se l'host contiene '\' (instance name) o '(localdb)', non aggiungere la porta
|
|
bool hasInstanceName = credential.Host.Contains('\\') ||
|
|
credential.Host.StartsWith("(localdb)", StringComparison.OrdinalIgnoreCase);
|
|
|
|
if (hasInstanceName)
|
|
{
|
|
// Per named instances e LocalDB, non includere la porta
|
|
builder.Add($"Server={credential.Host}");
|
|
}
|
|
else
|
|
{
|
|
// Per connessioni TCP/IP standard, include host e porta
|
|
// Ma solo se la porta non è la default (1433) per localhost
|
|
if ((credential.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase) ||
|
|
credential.Host == "." ||
|
|
credential.Host == "127.0.0.1") && credential.Port == 1433)
|
|
{
|
|
// Per localhost con porta default, ometti la porta per usare Named Pipes
|
|
builder.Add($"Server={credential.Host}");
|
|
}
|
|
else
|
|
{
|
|
// Per altri casi, usa host,porta
|
|
builder.Add($"Server={credential.Host},{credential.Port}");
|
|
}
|
|
}
|
|
|
|
// Se username è vuoto o è "Integrated", usa Windows Authentication
|
|
if (string.IsNullOrWhiteSpace(credential.Username) ||
|
|
credential.Username.Equals("Integrated", StringComparison.OrdinalIgnoreCase) ||
|
|
credential.Username.Equals("Windows", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
builder.Add("Integrated Security=True");
|
|
}
|
|
else
|
|
{
|
|
// Usa SQL Server Authentication
|
|
builder.Add($"User Id={credential.Username}");
|
|
builder.Add($"Password={credential.Password}");
|
|
}
|
|
|
|
builder.Add($"Connection Timeout={credential.CommandTimeout}");
|
|
|
|
// Aggiungi Database solo se specificato
|
|
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
|
builder.Add($"Database={credential.DatabaseName}");
|
|
|
|
if (credential.IgnoreSslErrors)
|
|
builder.Add("TrustServerCertificate=True");
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
} private static string BuildMySqlConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>
|
|
{
|
|
$"Server={credential.Host}",
|
|
$"Port={credential.Port}",
|
|
$"Uid={credential.Username}",
|
|
$"Pwd={credential.Password}",
|
|
$"Connection Timeout={credential.CommandTimeout}"
|
|
};
|
|
|
|
// Aggiungi Database solo se specificato
|
|
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
|
builder.Add($"Database={credential.DatabaseName}");
|
|
|
|
if (credential.IgnoreSslErrors)
|
|
builder.Add("SslMode=None");
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
} private static string BuildPostgreSqlConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>
|
|
{
|
|
$"Host={credential.Host}",
|
|
$"Port={credential.Port}",
|
|
$"Username={credential.Username}",
|
|
$"Password={credential.Password}",
|
|
$"Timeout={credential.CommandTimeout}"
|
|
};
|
|
|
|
// Aggiungi Database solo se specificato
|
|
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
|
builder.Add($"Database={credential.DatabaseName}");
|
|
|
|
if (credential.IgnoreSslErrors)
|
|
builder.Add("SSL Mode=Disable");
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
} private static string BuildOracleConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>();
|
|
|
|
// Per Oracle, il formato cambia se c'è o meno un database specificato
|
|
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
|
{
|
|
builder.Add($"Data Source={credential.Host}:{credential.Port}/{credential.DatabaseName}");
|
|
}
|
|
else
|
|
{
|
|
builder.Add($"Data Source={credential.Host}:{credential.Port}");
|
|
}
|
|
|
|
builder.AddRange(new[]
|
|
{
|
|
$"User Id={credential.Username}",
|
|
$"Password={credential.Password}",
|
|
$"Connection Timeout={credential.CommandTimeout}"
|
|
});
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static string BuildSqliteConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>
|
|
{
|
|
$"Data Source={credential.DatabaseName}"
|
|
};
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static string BuildDb2ConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>
|
|
{
|
|
$"Server={credential.Host}:{credential.Port}",
|
|
$"Database={credential.DatabaseName}",
|
|
$"UID={credential.Username}",
|
|
$"PWD={credential.Password}",
|
|
$"Connection Timeout={credential.CommandTimeout}"
|
|
};
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static string BuildSapHanaConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>
|
|
{
|
|
$"Server={credential.Host}:{credential.Port}",
|
|
$"DatabaseName={credential.DatabaseName}",
|
|
$"UserID={credential.Username}",
|
|
$"Password={credential.Password}",
|
|
$"Connection Timeout={credential.CommandTimeout}"
|
|
};
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static string BuildOdbcConnectionString(DatabaseCredential credential)
|
|
{
|
|
// Se è già presente una connection string personalizzata, utilizzala
|
|
if (!string.IsNullOrEmpty(credential.ConnectionString))
|
|
return credential.ConnectionString;
|
|
|
|
var builder = new List<string>();
|
|
|
|
// Modalità DSN: usa il DSN configurato
|
|
if (credential.OdbcMode == OdbcConnectionMode.Dsn && !string.IsNullOrEmpty(credential.OdbcDsnName))
|
|
{
|
|
builder.Add($"DSN={credential.OdbcDsnName}");
|
|
|
|
// Aggiungi credenziali se fornite
|
|
if (!string.IsNullOrEmpty(credential.Username))
|
|
builder.Add($"UID={credential.Username}");
|
|
|
|
if (!string.IsNullOrEmpty(credential.Password))
|
|
builder.Add($"PWD={credential.Password}");
|
|
}
|
|
// Modalità Custom: costruisci manualmente la connection string
|
|
else
|
|
{
|
|
// Driver (se specificato nei parametri aggiuntivi)
|
|
if (credential.AdditionalParameters?.ContainsKey("Driver") == true)
|
|
{
|
|
builder.Add($"Driver={{{credential.AdditionalParameters["Driver"]}}}");
|
|
}
|
|
|
|
// Server/Host
|
|
if (!string.IsNullOrEmpty(credential.Host))
|
|
{
|
|
builder.Add($"Server={credential.Host}");
|
|
|
|
// Porta (se diversa da 0)
|
|
if (credential.Port > 0)
|
|
builder.Add($"Port={credential.Port}");
|
|
}
|
|
|
|
// Database
|
|
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
|
builder.Add($"Database={credential.DatabaseName}");
|
|
|
|
// Credenziali
|
|
if (!string.IsNullOrEmpty(credential.Username))
|
|
builder.Add($"UID={credential.Username}");
|
|
|
|
if (!string.IsNullOrEmpty(credential.Password))
|
|
builder.Add($"PWD={credential.Password}");
|
|
}
|
|
|
|
// Timeout
|
|
if (credential.CommandTimeout > 0)
|
|
builder.Add($"Connection Timeout={credential.CommandTimeout}");
|
|
|
|
// Parametri aggiuntivi (escludendo Driver se già aggiunto)
|
|
if (credential.AdditionalParameters != null)
|
|
{
|
|
foreach (var param in credential.AdditionalParameters)
|
|
{
|
|
if (param.Key != "Driver") // Driver già gestito sopra
|
|
builder.Add($"{param.Key}={param.Value}");
|
|
}
|
|
}
|
|
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static void AddAdditionalParameters(List<string> builder, Dictionary<string, string>? additionalParams)
|
|
{
|
|
if (additionalParams != null)
|
|
{
|
|
foreach (var param in additionalParams)
|
|
{
|
|
builder.Add($"{param.Key}={param.Value}");
|
|
}
|
|
}
|
|
}
|
|
}
|