feat: Integrazione completa gestione credenziali per database e REST API con supporto SAP B1 e Salesforce
NUOVE FUNZIONALITÀ: - Aggiunto modulo CredentialManager per gestione centralizzata credenziali - Implementata UI Blazor per gestione credenziali (CredentialManagement.razor) - Supporto completo per credenziali database (SQL Server, MySQL, PostgreSQL, Oracle, SQLite, DB2, SAP HANA) - Gestione unificata REST API con supporto specifico per SAP B1 Service Layer e Salesforce - Test reali di connessione per database, SAP B1 e Salesforce OAuth2 - Selezione dinamica tipo servizio REST (Generico, SAP B1, Salesforce) con campi specifici - Persistenza sicura di credenziali con crittografia password e campi sensibili COMPONENTI AGGIUNTI: - CredentialManager/Models/: CredentialEntity, CredentialModels (DatabaseCredential, RestApiCredential, SapB1ServiceLayerCredential, SalesforceCredential) - CredentialManager/Services/: CredentialService, EncryptionService, DatabaseInitializer - CredentialManager/Data/: CredentialDbContext con Entity Framework - DataConnection/CredentialManagement/: Interfacce e servizi di integrazione - Data_Coupler/Pages/CredentialManagement.razor: UI completa per gestione credenziali MIGLIORAMENTI UI: - Form dinamica per REST API con campi specifici per tipo servizio - Validazione campi obbligatori per Salesforce (ClientId, ClientSecret, SecurityToken) - Test connessione in tempo reale dalla modale di inserimento/modifica - Rimozione sezioni separate per SAP B1 e Salesforce (ora unificate in REST API) - Gestione stato loading durante operazioni async PERSISTENZA AVANZATA: - Campo RestServiceType aggiunto a CredentialEntity con migrazione automatica - Serializzazione campi specifici Salesforce/SAP B1 in AdditionalParameters JSON - Mapping bidirezionale tra entità database e modelli business - Gestione nullability e conversioni tipo sicure SICUREZZA: - Crittografia AES-256 per password e token sensibili - Gestione sicura ConnectionString database - Validazione input e sanitizzazione dati TESTING E CONNETTIVITÀ: - Test autenticazione reale SAP B1 Service Layer - Test OAuth2 Salesforce con supporto Connected App - Test connettività database multi-provider - Logging dettagliato per debugging e monitoraggio CONFIGURAZIONE: - Dependency injection per tutti i servizi - Configurazione Entity Framework con SQLite - Tasks VS Code per build e run - Gestione connection string centralizzata CORREZIONI: - Risolti errori nullability in CredentialService - Aggiunto using Microsoft.JSInterop per IJSRuntime - Fix compilazione e warning Files modificati: 35+ file tra nuovi e aggiornati
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using CredentialManager.Models;
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using CredentialManager.Models;
|
||||
|
||||
namespace DataConnection.CredentialManagement.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods per convertire le credenziali CredentialManager in oggetti DataConnection
|
||||
/// </summary>
|
||||
public static class CredentialExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converte il tipo di database da CredentialManager.Models.DatabaseType a DataConnection.Enums.DatabaseType
|
||||
/// </summary>
|
||||
public static DataConnection.Enums.DatabaseType ToDataConnectionDatabaseType(this CredentialManager.Models.DatabaseType credentialDbType)
|
||||
{
|
||||
return credentialDbType switch
|
||||
{
|
||||
CredentialManager.Models.DatabaseType.SqlServer => DataConnection.Enums.DatabaseType.SqlServer,
|
||||
CredentialManager.Models.DatabaseType.MySql => DataConnection.Enums.DatabaseType.MySql,
|
||||
CredentialManager.Models.DatabaseType.PostgreSql => DataConnection.Enums.DatabaseType.PostgreSql,
|
||||
CredentialManager.Models.DatabaseType.Oracle => DataConnection.Enums.DatabaseType.Oracle,
|
||||
CredentialManager.Models.DatabaseType.Sqlite => DataConnection.Enums.DatabaseType.Sqlite,
|
||||
CredentialManager.Models.DatabaseType.DB2 => DataConnection.Enums.DatabaseType.DB2,
|
||||
CredentialManager.Models.DatabaseType.SapHana => DataConnection.Enums.DatabaseType.SapHana,
|
||||
_ => throw new NotSupportedException($"Database type {credentialDbType} not supported")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converte il tipo di database da DataConnection.Enums.DatabaseType a CredentialManager.Models.DatabaseType
|
||||
/// </summary>
|
||||
public static CredentialManager.Models.DatabaseType ToCredentialDatabaseType(this DataConnection.Enums.DatabaseType dataConnectionDbType)
|
||||
{
|
||||
return dataConnectionDbType switch
|
||||
{
|
||||
DataConnection.Enums.DatabaseType.SqlServer => CredentialManager.Models.DatabaseType.SqlServer,
|
||||
DataConnection.Enums.DatabaseType.MySql => CredentialManager.Models.DatabaseType.MySql,
|
||||
DataConnection.Enums.DatabaseType.PostgreSql => CredentialManager.Models.DatabaseType.PostgreSql,
|
||||
DataConnection.Enums.DatabaseType.Oracle => CredentialManager.Models.DatabaseType.Oracle,
|
||||
DataConnection.Enums.DatabaseType.Sqlite => CredentialManager.Models.DatabaseType.Sqlite,
|
||||
DataConnection.Enums.DatabaseType.DB2 => CredentialManager.Models.DatabaseType.DB2,
|
||||
DataConnection.Enums.DatabaseType.SapHana => CredentialManager.Models.DatabaseType.SapHana,
|
||||
_ => throw new NotSupportedException($"Database type {dataConnectionDbType} not supported")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crea una DatabaseCredential da parametri di connessione
|
||||
/// </summary>
|
||||
public static DatabaseCredential CreateDatabaseCredential(
|
||||
string name,
|
||||
DataConnection.Enums.DatabaseType databaseType,
|
||||
string host,
|
||||
int port,
|
||||
string databaseName,
|
||||
string username,
|
||||
string password,
|
||||
int commandTimeout = 30,
|
||||
bool ignoreSslErrors = false)
|
||||
{
|
||||
return new DatabaseCredential
|
||||
{
|
||||
Name = name,
|
||||
DatabaseType = databaseType.ToCredentialDatabaseType(),
|
||||
Host = host,
|
||||
Port = port,
|
||||
DatabaseName = databaseName,
|
||||
Username = username,
|
||||
Password = password,
|
||||
CommandTimeout = commandTimeout,
|
||||
IgnoreSslErrors = ignoreSslErrors
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crea una RestApiCredential da parametri di connessione
|
||||
/// </summary>
|
||||
public static RestApiCredential CreateRestApiCredential(
|
||||
string name,
|
||||
string baseUrl,
|
||||
string? apiKey = null,
|
||||
string? username = null,
|
||||
string? password = null,
|
||||
string? authToken = null,
|
||||
int timeoutSeconds = 100,
|
||||
bool ignoreSslErrors = false,
|
||||
Dictionary<string, string>? headers = null)
|
||||
{
|
||||
return new RestApiCredential
|
||||
{
|
||||
Name = name,
|
||||
BaseUrl = baseUrl,
|
||||
ApiKey = apiKey,
|
||||
Username = username,
|
||||
Password = password,
|
||||
AuthToken = authToken,
|
||||
TimeoutSeconds = timeoutSeconds,
|
||||
IgnoreSslErrors = ignoreSslErrors,
|
||||
Headers = headers
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using CredentialManager;
|
||||
using CredentialManager.Services;
|
||||
using CredentialManager.Data;
|
||||
using DataConnection.CredentialManagement.Interfaces;
|
||||
using DataConnection.CredentialManagement.Services;
|
||||
|
||||
namespace DataConnection.CredentialManagement;
|
||||
|
||||
/// <summary>
|
||||
/// Metodi di estensione per configurare i servizi di gestione credenziali per DataConnection
|
||||
/// </summary>
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Aggiunge i servizi di gestione credenziali a DataConnection
|
||||
/// </summary>
|
||||
/// <param name="services">La collezione di servizi</param>
|
||||
/// <param name="connectionString">Stringa di connessione per il database delle credenziali</param>
|
||||
/// <returns>La collezione di servizi per il chaining</returns>
|
||||
public static IServiceCollection AddDataConnectionCredentialManagement(
|
||||
this IServiceCollection services,
|
||||
string connectionString = "Data Source=credentials.db")
|
||||
{
|
||||
// Estrai il percorso del database dalla connection string
|
||||
string databasePath;
|
||||
if (connectionString.StartsWith("Data Source=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
databasePath = connectionString.Substring("Data Source=".Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Se non è una connection string, assumiamo che sia già un percorso
|
||||
databasePath = connectionString;
|
||||
}
|
||||
|
||||
// Aggiungi i servizi base di CredentialManager
|
||||
services.AddCredentialManager(databasePath);
|
||||
|
||||
// Aggiungi il servizio di integrazione DataConnection
|
||||
services.AddScoped<IDataConnectionCredentialService, DataConnectionCredentialService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggiunge i servizi di gestione credenziali con configurazione avanzata
|
||||
/// </summary>
|
||||
/// <param name="services">La collezione di servizi</param>
|
||||
/// <param name="configure">Azione per configurare le opzioni</param>
|
||||
/// <returns>La collezione di servizi per il chaining</returns>
|
||||
public static IServiceCollection AddDataConnectionCredentialManagement(
|
||||
this IServiceCollection services,
|
||||
Action<DataConnectionCredentialOptions> configure)
|
||||
{
|
||||
var options = new DataConnectionCredentialOptions();
|
||||
configure(options);
|
||||
|
||||
return services.AddDataConnectionCredentialManagement(options.ConnectionString);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opzioni per la configurazione del DataConnectionCredentialManagement
|
||||
/// </summary>
|
||||
public class DataConnectionCredentialOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Stringa di connessione per il database delle credenziali
|
||||
/// </summary>
|
||||
public string ConnectionString { get; set; } = "Data Source=credentials.db";
|
||||
|
||||
/// <summary>
|
||||
/// Abilita il logging dettagliato
|
||||
/// </summary>
|
||||
public bool EnableDetailedLogging { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Timeout per le operazioni sul database (in secondi)
|
||||
/// </summary>
|
||||
public int DatabaseTimeout { get; set; } = 30;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interfaccia per il servizio di gestione credenziali specifico per DataConnection
|
||||
/// Questa interfaccia estende le funzionalità base di CredentialManager
|
||||
/// con metodi specifici per l'integrazione con DataConnection
|
||||
/// </summary>
|
||||
public interface IDataConnectionCredentialServiceConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Configura il servizio con le opzioni specificate
|
||||
/// </summary>
|
||||
/// <param name="options">Le opzioni di configurazione</param>
|
||||
void Configure(DataConnectionCredentialOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Verifica la connessione al database delle credenziali
|
||||
/// </summary>
|
||||
/// <returns>True se la connessione è valida</returns>
|
||||
Task<bool> TestConnectionAsync();
|
||||
}
|
||||
@@ -0,0 +1,858 @@
|
||||
using CredentialManager.Models;
|
||||
using CredentialManager.Services;
|
||||
using CredentialManager.Integration;
|
||||
using DataConnection.CredentialManagement.Interfaces;
|
||||
using DataConnection.CredentialManagement.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace DataConnection.CredentialManagement.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Servizio per l'integrazione delle credenziali con DataConnection
|
||||
/// </summary>
|
||||
public class DataConnectionCredentialService : IDataConnectionCredentialService
|
||||
{
|
||||
private readonly ICredentialService _credentialService;
|
||||
private readonly ILogger<DataConnectionCredentialService> _logger;
|
||||
|
||||
public DataConnectionCredentialService(
|
||||
ICredentialService credentialService,
|
||||
ILogger<DataConnectionCredentialService> logger)
|
||||
{
|
||||
_credentialService = credentialService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
#region Database Credentials
|
||||
|
||||
public async Task<DatabaseCredential?> GetDatabaseCredentialAsync(string name)
|
||||
{
|
||||
return await _credentialService.GetDatabaseCredentialAsync(name);
|
||||
}
|
||||
|
||||
public async Task<DatabaseCredential?> GetDatabaseCredentialAsync(int id)
|
||||
{
|
||||
return await _credentialService.GetDatabaseCredentialAsync(id);
|
||||
}
|
||||
|
||||
public async Task<List<DatabaseCredential>> GetAllDatabaseCredentialsAsync()
|
||||
{
|
||||
return await _credentialService.GetAllDatabaseCredentialsAsync();
|
||||
}
|
||||
|
||||
public async Task<int> SaveDatabaseCredentialAsync(DatabaseCredential credential)
|
||||
{
|
||||
_logger.LogInformation("Saving database credential: {Name}", credential.Name);
|
||||
return await _credentialService.SaveDatabaseCredentialAsync(credential);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteDatabaseCredentialAsync(int id)
|
||||
{
|
||||
_logger.LogInformation("Deleting database credential with ID: {Id}", id);
|
||||
return await _credentialService.DeleteCredentialAsync(id);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteDatabaseCredentialAsync(string name)
|
||||
{
|
||||
_logger.LogInformation("Deleting database credential: {Name}", name);
|
||||
return await _credentialService.DeleteCredentialAsync(name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region REST API Credentials
|
||||
|
||||
public async Task<RestApiCredential?> GetRestApiCredentialAsync(string name)
|
||||
{
|
||||
return await _credentialService.GetRestApiCredentialAsync(name);
|
||||
}
|
||||
|
||||
public async Task<RestApiCredential?> GetRestApiCredentialAsync(int id)
|
||||
{
|
||||
return await _credentialService.GetRestApiCredentialAsync(id);
|
||||
}
|
||||
|
||||
public async Task<List<RestApiCredential>> GetAllRestApiCredentialsAsync()
|
||||
{
|
||||
return await _credentialService.GetAllRestApiCredentialsAsync();
|
||||
}
|
||||
|
||||
public async Task<int> SaveRestApiCredentialAsync(RestApiCredential credential)
|
||||
{
|
||||
_logger.LogInformation("Saving REST API credential: {Name}", credential.Name);
|
||||
return await _credentialService.SaveRestApiCredentialAsync(credential);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteRestApiCredentialAsync(int id)
|
||||
{
|
||||
_logger.LogInformation("Deleting REST API credential with ID: {Id}", id);
|
||||
return await _credentialService.DeleteCredentialAsync(id);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteRestApiCredentialAsync(string name)
|
||||
{
|
||||
_logger.LogInformation("Deleting REST API credential: {Name}", name);
|
||||
return await _credentialService.DeleteCredentialAsync(name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataConnection Specific Operations
|
||||
|
||||
public async Task<string> GetConnectionStringAsync(string credentialName)
|
||||
{
|
||||
var credential = await GetDatabaseCredentialAsync(credentialName);
|
||||
if (credential == null)
|
||||
throw new InvalidOperationException($"Database credential '{credentialName}' not found");
|
||||
|
||||
return credential.ToConnectionString();
|
||||
}
|
||||
|
||||
public async Task<string> GetConnectionStringAsync(int credentialId)
|
||||
{
|
||||
var credential = await GetDatabaseCredentialAsync(credentialId);
|
||||
if (credential == null)
|
||||
throw new InvalidOperationException($"Database credential with ID '{credentialId}' not found"); return credential.ToConnectionString();
|
||||
}
|
||||
public async Task<DataConnection.EF.DbManagerOptions> GetDbManagerOptionsAsync(string credentialName)
|
||||
{
|
||||
var credential = await GetDatabaseCredentialAsync(credentialName);
|
||||
if (credential == null)
|
||||
throw new InvalidOperationException($"Database credential '{credentialName}' not found");
|
||||
|
||||
var options = new DataConnection.EF.DbManagerOptions
|
||||
{
|
||||
ServerConnectionString = credential.ToConnectionString(),
|
||||
DatabaseName = credential.DatabaseName!,
|
||||
DatabaseType = credential.DatabaseType.ToDataConnectionDatabaseType(),
|
||||
CommandTimeout = credential.CommandTimeout
|
||||
};
|
||||
|
||||
// Configura automaticamente il servizio di scoperta database
|
||||
options.ConfigureDatabaseDiscovery(options.DatabaseType);
|
||||
|
||||
_logger.LogDebug("Created DbManagerOptions for credential: {Name} ({DatabaseType})",
|
||||
credentialName, credential.DatabaseType);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public async Task<DataConnection.EF.DbManagerOptions> GetDbManagerOptionsAsync(int credentialId)
|
||||
{
|
||||
var credential = await GetDatabaseCredentialAsync(credentialId);
|
||||
if (credential == null)
|
||||
throw new InvalidOperationException($"Database credential with ID '{credentialId}' not found"); var options = new DataConnection.EF.DbManagerOptions
|
||||
{
|
||||
ServerConnectionString = credential.ToConnectionString(),
|
||||
DatabaseName = credential.DatabaseName!,
|
||||
DatabaseType = credential.DatabaseType.ToDataConnectionDatabaseType(),
|
||||
CommandTimeout = credential.CommandTimeout
|
||||
};
|
||||
|
||||
// Configura automaticamente il servizio di scoperta database
|
||||
options.ConfigureDatabaseDiscovery(options.DatabaseType);
|
||||
|
||||
_logger.LogDebug("Created DbManagerOptions for credential ID: {Id} ({DatabaseType})",
|
||||
credentialId, credential.DatabaseType);
|
||||
return options;
|
||||
}
|
||||
|
||||
public async Task<DataConnection.REST.Configuration.RestServiceOptions> GetRestServiceOptionsAsync(string credentialName)
|
||||
{
|
||||
var credential = await GetRestApiCredentialAsync(credentialName);
|
||||
if (credential == null)
|
||||
throw new InvalidOperationException($"REST API credential '{credentialName}' not found");
|
||||
|
||||
var options = new DataConnection.REST.Configuration.RestServiceOptions
|
||||
{
|
||||
BaseUrl = credential.BaseUrl,
|
||||
ApiKey = credential.ApiKey,
|
||||
Username = credential.Username,
|
||||
Password = credential.Password,
|
||||
AuthToken = credential.AuthToken,
|
||||
TimeoutSeconds = credential.TimeoutSeconds,
|
||||
IgnoreSslErrors = credential.IgnoreSslErrors
|
||||
};
|
||||
|
||||
_logger.LogDebug("Created RestServiceOptions for credential: {Name} ({BaseUrl})",
|
||||
credentialName, credential.BaseUrl);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public async Task<DataConnection.REST.Configuration.RestServiceOptions> GetRestServiceOptionsAsync(int credentialId)
|
||||
{
|
||||
var credential = await GetRestApiCredentialAsync(credentialId);
|
||||
if (credential == null)
|
||||
throw new InvalidOperationException($"REST API credential with ID '{credentialId}' not found");
|
||||
|
||||
var options = new DataConnection.REST.Configuration.RestServiceOptions
|
||||
{
|
||||
BaseUrl = credential.BaseUrl,
|
||||
ApiKey = credential.ApiKey,
|
||||
Username = credential.Username,
|
||||
Password = credential.Password,
|
||||
AuthToken = credential.AuthToken,
|
||||
TimeoutSeconds = credential.TimeoutSeconds,
|
||||
IgnoreSslErrors = credential.IgnoreSslErrors
|
||||
};
|
||||
|
||||
_logger.LogDebug("Created RestServiceOptions for credential ID: {Id} ({BaseUrl})",
|
||||
credentialId, credential.BaseUrl);
|
||||
return options;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Connection Testing
|
||||
|
||||
public async Task<(bool Success, string Message)> TestDatabaseConnectionAsync(string credentialName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var credential = await GetDatabaseCredentialAsync(credentialName);
|
||||
if (credential == null)
|
||||
return (false, $"Credenziale '{credentialName}' non trovata");
|
||||
|
||||
return await TestDatabaseConnectionAsync(credential);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel test della connessione per credenziale: {Name}", credentialName);
|
||||
return (false, $"Errore nel test: {ex.Message}");
|
||||
}
|
||||
}
|
||||
public async Task<(bool Success, string Message)> TestDatabaseConnectionAsync(DatabaseCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
var connectionString = ConnectionStringBuilder.BuildConnectionString(credential);
|
||||
|
||||
// Test base della sintassi
|
||||
if (string.IsNullOrEmpty(connectionString))
|
||||
return (false, "Connection string vuota o non valida");
|
||||
|
||||
_logger.LogInformation("Testing database connection for {Name} ({DatabaseType})",
|
||||
credential.Name, credential.DatabaseType);
|
||||
|
||||
// Test di connessione diretto basato sul tipo di database
|
||||
try
|
||||
{
|
||||
return credential.DatabaseType switch
|
||||
{
|
||||
CredentialManager.Models.DatabaseType.SqlServer => await TestSqlServerConnection(connectionString, credential),
|
||||
CredentialManager.Models.DatabaseType.MySql => await TestMySqlConnection(connectionString, credential),
|
||||
CredentialManager.Models.DatabaseType.PostgreSql => await TestPostgreSqlConnection(connectionString, credential),
|
||||
CredentialManager.Models.DatabaseType.Oracle => await TestOracleConnection(connectionString, credential),
|
||||
CredentialManager.Models.DatabaseType.Sqlite => await TestSqliteConnection(connectionString, credential),
|
||||
_ => (false, $"Test di connessione non implementato per {credential.DatabaseType}")
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Database connection test failed for {Name}", credential.Name);
|
||||
return (false, $"Errore di connessione: {ex.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel test della connessione database");
|
||||
return (false, $"Errore: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool Success, string Message)> TestSqlServerConnection(string connectionString, DatabaseCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var connection = new Microsoft.Data.SqlClient.SqlConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
var command = connection.CreateCommand();
|
||||
command.CommandText = "SELECT @@VERSION";
|
||||
command.CommandTimeout = credential.CommandTimeout;
|
||||
var version = await command.ExecuteScalarAsync();
|
||||
|
||||
return (true, $"Connessione SQL Server riuscita!\n\nDettagli:\n- Host: {credential.Host}:{credential.Port}\n- Database: {credential.DatabaseName ?? "Default"}\n- Versione: {version?.ToString()?.Split('\n')[0]}\n- Timeout: {credential.CommandTimeout}s");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, $"Errore SQL Server: {ex.Message}");
|
||||
}
|
||||
}
|
||||
private Task<(bool Success, string Message)> TestMySqlConnection(string connectionString, DatabaseCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Per MySQL, dovremmo usare MySql.Data.MySqlClient o Pomelo.EntityFrameworkCore.MySql
|
||||
// Per ora returnamo un messaggio che indica che il test non è implementato
|
||||
return Task.FromResult<(bool Success, string Message)>((false, "Test MySQL non implementato - installare MySql.Data.MySqlClient"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult<(bool Success, string Message)>((false, $"Errore MySQL: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
|
||||
private Task<(bool Success, string Message)> TestPostgreSqlConnection(string connectionString, DatabaseCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Per PostgreSQL, dovremmo usare Npgsql
|
||||
return Task.FromResult<(bool Success, string Message)>((false, "Test PostgreSQL non implementato - installare Npgsql"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult<(bool Success, string Message)>((false, $"Errore PostgreSQL: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
|
||||
private Task<(bool Success, string Message)> TestOracleConnection(string connectionString, DatabaseCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Per Oracle, dovremmo usare Oracle.ManagedDataAccess
|
||||
return Task.FromResult<(bool Success, string Message)>((false, "Test Oracle non implementato - installare Oracle.ManagedDataAccess"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult<(bool Success, string Message)>((false, $"Errore Oracle: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool Success, string Message)> TestSqliteConnection(string connectionString, DatabaseCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var connection = new Microsoft.Data.Sqlite.SqliteConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
var command = connection.CreateCommand();
|
||||
command.CommandText = "SELECT sqlite_version()";
|
||||
command.CommandTimeout = credential.CommandTimeout;
|
||||
var version = await command.ExecuteScalarAsync();
|
||||
|
||||
return (true, $"Connessione SQLite riuscita!\n\nDettagli:\n- File: {credential.Host ?? connectionString}\n- Versione SQLite: {version}\n- Timeout: {credential.CommandTimeout}s");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, $"Errore SQLite: {ex.Message}");
|
||||
}
|
||||
}
|
||||
public async Task<(bool Success, string Message)> TestRestApiConnectionAsync(string credentialName)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Attempting to test REST API connection for credential: {Name}", credentialName);
|
||||
|
||||
var credential = await GetRestApiCredentialAsync(credentialName);
|
||||
if (credential == null)
|
||||
{
|
||||
_logger.LogWarning("REST API credential '{Name}' not found", credentialName);
|
||||
return (false, $"Credenziale REST API '{credentialName}' non trovata");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Found credential: {Name}, ServiceType: {ServiceType}, BaseUrl: {BaseUrl}",
|
||||
credential.Name, credential.ServiceType, credential.BaseUrl);
|
||||
|
||||
return await TestRestApiConnectionAsync(credential);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel test della connessione REST API per credenziale: {Name}", credentialName);
|
||||
return (false, $"Errore nel test: {ex.Message}");
|
||||
}
|
||||
}
|
||||
public async Task<(bool Success, string Message)> TestRestApiConnectionAsync(RestApiCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(credential.BaseUrl))
|
||||
return (false, "Base URL non specificata");
|
||||
|
||||
// Test base dell'URL
|
||||
if (!Uri.TryCreate(credential.BaseUrl, UriKind.Absolute, out var uri))
|
||||
return (false, "Base URL non valida");
|
||||
|
||||
// Per i servizi specifici, eseguiamo test di autenticazione reali
|
||||
switch (credential.ServiceType)
|
||||
{
|
||||
case RestServiceType.SapB1ServiceLayer:
|
||||
return await TestSapB1ServiceLayerAuthentication(credential);
|
||||
|
||||
case RestServiceType.Salesforce:
|
||||
return await TestSalesforceAuthentication(credential);
|
||||
|
||||
default:
|
||||
// Per REST API generiche, facciamo un test di connettività base
|
||||
return await TestGenericRestApiConnection(credential);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel test della connessione REST API");
|
||||
return (false, $"Errore: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool Success, string Message)> TestSapB1ServiceLayerAuthentication(RestApiCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Testing SAP B1 Service Layer authentication for {Name} ({BaseUrl})",
|
||||
credential.Name, credential.BaseUrl);
|
||||
|
||||
using var httpClient = new HttpClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(credential.TimeoutSeconds);
|
||||
|
||||
// Test di login al Service Layer
|
||||
var loginUrl = credential.BaseUrl.TrimEnd('/') + "/Login";
|
||||
|
||||
var loginData = new
|
||||
{
|
||||
CompanyDB = credential.CompanyDatabase,
|
||||
UserName = credential.Username,
|
||||
Password = credential.Password,
|
||||
Language = credential.Language ?? "en-US"
|
||||
};
|
||||
|
||||
var loginJson = System.Text.Json.JsonSerializer.Serialize(loginData);
|
||||
var loginContent = new StringContent(loginJson, System.Text.Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await httpClient.PostAsync(loginUrl, loginContent);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return (true, $"Autenticazione SAP B1 Service Layer riuscita!\n\nDettagli:\n- Server: {credential.BaseUrl}\n- Company DB: {credential.CompanyDatabase}\n- Versione: {credential.Version}\n- Lingua: {credential.Language}\n- Timeout: {credential.TimeoutSeconds}s");
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
return (false, $"Autenticazione SAP B1 fallita. Status: {response.StatusCode}\nDettagli: {errorContent}");
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return (false, $"Errore di rete SAP B1: {ex.Message}");
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException || ex.Message.Contains("timeout"))
|
||||
{
|
||||
return (false, $"Timeout della connessione SAP B1 ({credential.TimeoutSeconds}s)");
|
||||
}
|
||||
}
|
||||
private async Task<(bool Success, string Message)> TestSalesforceAuthentication(RestApiCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Testing Salesforce authentication for {Name} ({BaseUrl})",
|
||||
credential.Name, credential.BaseUrl);
|
||||
|
||||
_logger.LogDebug("Salesforce credential details: Username={Username}, HasPassword={HasPassword}, HasSecurityToken={HasSecurityToken}, HasClientId={HasClientId}, HasClientSecret={HasClientSecret}",
|
||||
credential.Username, !string.IsNullOrEmpty(credential.Password), !string.IsNullOrEmpty(credential.SecurityToken),
|
||||
!string.IsNullOrEmpty(credential.ClientId), !string.IsNullOrEmpty(credential.ClientSecret));
|
||||
|
||||
using var httpClient = new HttpClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(credential.TimeoutSeconds);
|
||||
|
||||
// Test di autenticazione OAuth2
|
||||
var tokenUrl = credential.BaseUrl.TrimEnd('/') + "/services/oauth2/token";
|
||||
var tokenData = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new("grant_type", "password"),
|
||||
new("username", credential.Username ?? "")
|
||||
};
|
||||
|
||||
// Aggiungiamo password + security token se disponibile
|
||||
var password = credential.Password ?? "";
|
||||
if (!string.IsNullOrEmpty(credential.SecurityToken))
|
||||
{
|
||||
password += credential.SecurityToken;
|
||||
}
|
||||
tokenData.Add(new("password", password));
|
||||
|
||||
// Aggiungiamo client credentials se disponibili
|
||||
if (!string.IsNullOrEmpty(credential.ClientId))
|
||||
{
|
||||
tokenData.Add(new("client_id", credential.ClientId));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(credential.ClientSecret))
|
||||
{
|
||||
tokenData.Add(new("client_secret", credential.ClientSecret));
|
||||
}
|
||||
|
||||
_logger.LogDebug("Posting to Salesforce token URL: {TokenUrl}", tokenUrl);
|
||||
|
||||
var tokenContent = new FormUrlEncodedContent(tokenData);
|
||||
var response = await httpClient.PostAsync(tokenUrl, tokenContent);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
_logger.LogInformation("Salesforce authentication successful for {Name}", credential.Name);
|
||||
return (true, $"Autenticazione Salesforce riuscita!\n\nDettagli:\n- Login URL: {credential.BaseUrl}\n- API Version: {credential.ApiVersion}\n- Sandbox: {credential.IsSandbox}\n- Tipo Auth: OAuth2\n- Timeout: {credential.TimeoutSeconds}s");
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
_logger.LogWarning("Salesforce authentication failed for {Name}. Status: {StatusCode}, Response: {Response}",
|
||||
credential.Name, response.StatusCode, errorContent);
|
||||
return (false, $"Autenticazione Salesforce fallita. Status: {response.StatusCode}\nDettagli: {errorContent}");
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Network error during Salesforce authentication for {Name}", credential.Name);
|
||||
return (false, $"Errore di rete Salesforce: {ex.Message}");
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException || ex.Message.Contains("timeout"))
|
||||
{
|
||||
_logger.LogWarning("Timeout during Salesforce authentication for {Name} ({TimeoutSeconds}s)", credential.Name, credential.TimeoutSeconds);
|
||||
return (false, $"Timeout della connessione Salesforce ({credential.TimeoutSeconds}s)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected error during Salesforce authentication for {Name}", credential.Name);
|
||||
return (false, $"Errore imprevisto: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool Success, string Message)> TestGenericRestApiConnection(RestApiCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Test di connessione reale per REST API generiche
|
||||
using var httpClient = new HttpClient();
|
||||
|
||||
// Configurazione del timeout
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(credential.TimeoutSeconds);
|
||||
|
||||
// Configurazione autenticazione
|
||||
if (!string.IsNullOrEmpty(credential.ApiKey))
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Add("X-API-Key", credential.ApiKey);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(credential.AuthToken))
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {credential.AuthToken}");
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(credential.Username) && !string.IsNullOrEmpty(credential.Password))
|
||||
{
|
||||
var authValue = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{credential.Username}:{credential.Password}"));
|
||||
httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {authValue}");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Testing generic REST API connection for {Name} ({BaseUrl})",
|
||||
credential.Name, credential.BaseUrl);
|
||||
|
||||
// Facciamo una richiesta HEAD o GET semplice per testare la connettività
|
||||
var testEndpoint = credential.BaseUrl.TrimEnd('/') + "/";
|
||||
|
||||
var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, testEndpoint));
|
||||
|
||||
// Anche un 404 o 401 indica che il server è raggiungibile
|
||||
var isReachable = response.StatusCode != System.Net.HttpStatusCode.RequestTimeout &&
|
||||
!IsNetworkError(response.StatusCode);
|
||||
|
||||
if (isReachable)
|
||||
{
|
||||
return (true, $"Connessione REST API riuscita!\n\nDettagli:\n- URL: {credential.BaseUrl}\n- Status: {response.StatusCode}\n- Timeout: {credential.TimeoutSeconds}s\n- Auth: {GetAuthType(credential)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
return (false, $"Connessione fallita. Status: {response.StatusCode}\nVerifica URL e configurazione di rete.");
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Generic REST API connection test failed for {Name}", credential.Name);
|
||||
return (false, $"Errore di rete: {ex.Message}");
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException || ex.Message.Contains("timeout"))
|
||||
{
|
||||
return (false, $"Timeout della connessione ({credential.TimeoutSeconds}s). Il server potrebbe essere irraggiungibile.");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNetworkError(System.Net.HttpStatusCode statusCode)
|
||||
{
|
||||
return statusCode == System.Net.HttpStatusCode.RequestTimeout ||
|
||||
statusCode == System.Net.HttpStatusCode.BadGateway ||
|
||||
statusCode == System.Net.HttpStatusCode.ServiceUnavailable ||
|
||||
statusCode == System.Net.HttpStatusCode.GatewayTimeout;
|
||||
}
|
||||
|
||||
private static string GetAuthType(RestApiCredential credential)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(credential.ApiKey))
|
||||
return "API Key";
|
||||
if (!string.IsNullOrEmpty(credential.AuthToken))
|
||||
return "Auth Token";
|
||||
if (!string.IsNullOrEmpty(credential.Username))
|
||||
return "Basic Auth";
|
||||
return "Nessuna";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SAP B1 Service Layer Credentials
|
||||
|
||||
public async Task<SapB1ServiceLayerCredential?> GetSapB1CredentialAsync(string name)
|
||||
{
|
||||
return await _credentialService.GetSapB1CredentialAsync(name);
|
||||
}
|
||||
|
||||
public async Task<SapB1ServiceLayerCredential?> GetSapB1CredentialAsync(int id)
|
||||
{
|
||||
return await _credentialService.GetSapB1CredentialAsync(id);
|
||||
}
|
||||
|
||||
public async Task<List<SapB1ServiceLayerCredential>> GetAllSapB1CredentialsAsync()
|
||||
{
|
||||
return await _credentialService.GetAllSapB1CredentialsAsync();
|
||||
}
|
||||
|
||||
public async Task<int> SaveSapB1CredentialAsync(SapB1ServiceLayerCredential credential)
|
||||
{
|
||||
_logger.LogInformation("Saving SAP B1 Service Layer credential: {Name}", credential.Name);
|
||||
return await _credentialService.SaveSapB1CredentialAsync(credential);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteSapB1CredentialAsync(int id)
|
||||
{
|
||||
_logger.LogInformation("Deleting SAP B1 Service Layer credential with ID: {Id}", id);
|
||||
return await _credentialService.DeleteCredentialAsync(id);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteSapB1CredentialAsync(string name)
|
||||
{
|
||||
_logger.LogInformation("Deleting SAP B1 Service Layer credential: {Name}", name);
|
||||
return await _credentialService.DeleteCredentialAsync(name);
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Message)> TestSapB1ConnectionAsync(string credentialName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var credential = await GetSapB1CredentialAsync(credentialName);
|
||||
if (credential == null)
|
||||
return (false, $"Credenziale SAP B1 '{credentialName}' non trovata");
|
||||
|
||||
return await TestSapB1ConnectionAsync(credential);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel test della connessione SAP B1 per credenziale: {Name}", credentialName);
|
||||
return (false, $"Errore nel test: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Message)> TestSapB1ConnectionAsync(SapB1ServiceLayerCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(credential.ServerUrl))
|
||||
return (false, "Server URL non specificato");
|
||||
|
||||
if (!Uri.TryCreate(credential.ServerUrl, UriKind.Absolute, out var uri))
|
||||
return (false, "Server URL non valido");
|
||||
|
||||
_logger.LogInformation("Testing SAP B1 Service Layer connection for {Name} ({ServerUrl})",
|
||||
credential.Name, credential.ServerUrl);
|
||||
|
||||
using var httpClient = new HttpClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(credential.TimeoutSeconds);
|
||||
|
||||
// Test di accesso al Service Layer
|
||||
var loginUrl = credential.ServerUrl.TrimEnd('/') + "/Login";
|
||||
|
||||
var loginData = new
|
||||
{
|
||||
CompanyDB = credential.CompanyDatabase,
|
||||
UserName = credential.Username,
|
||||
Password = credential.Password,
|
||||
Language = credential.Language ?? "en-US"
|
||||
};
|
||||
|
||||
var loginJson = System.Text.Json.JsonSerializer.Serialize(loginData);
|
||||
var loginContent = new StringContent(loginJson, System.Text.Encoding.UTF8, "application/json");
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.PostAsync(loginUrl, loginContent);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return (true, $"Connessione SAP B1 Service Layer riuscita!\n\nDettagli:\n- Server: {credential.ServerUrl}\n- Company DB: {credential.CompanyDatabase}\n- Versione: {credential.Version}\n- Lingua: {credential.Language}\n- Timeout: {credential.TimeoutSeconds}s");
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
return (false, $"Login fallito. Status: {response.StatusCode}\nDettagli: {errorContent}");
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return (false, $"Errore di rete SAP B1: {ex.Message}");
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException || ex.Message.Contains("timeout"))
|
||||
{
|
||||
return (false, $"Timeout della connessione SAP B1 ({credential.TimeoutSeconds}s).");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel test della connessione SAP B1");
|
||||
return (false, $"Errore: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Salesforce Credentials
|
||||
|
||||
public async Task<SalesforceCredential?> GetSalesforceCredentialAsync(string name)
|
||||
{
|
||||
return await _credentialService.GetSalesforceCredentialAsync(name);
|
||||
}
|
||||
|
||||
public async Task<SalesforceCredential?> GetSalesforceCredentialAsync(int id)
|
||||
{
|
||||
return await _credentialService.GetSalesforceCredentialAsync(id);
|
||||
}
|
||||
|
||||
public async Task<List<SalesforceCredential>> GetAllSalesforceCredentialsAsync()
|
||||
{
|
||||
return await _credentialService.GetAllSalesforceCredentialsAsync();
|
||||
}
|
||||
|
||||
public async Task<int> SaveSalesforceCredentialAsync(SalesforceCredential credential)
|
||||
{
|
||||
_logger.LogInformation("Saving Salesforce credential: {Name}", credential.Name);
|
||||
return await _credentialService.SaveSalesforceCredentialAsync(credential);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteSalesforceCredentialAsync(int id)
|
||||
{
|
||||
_logger.LogInformation("Deleting Salesforce credential with ID: {Id}", id);
|
||||
return await _credentialService.DeleteCredentialAsync(id);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteSalesforceCredentialAsync(string name)
|
||||
{
|
||||
_logger.LogInformation("Deleting Salesforce credential: {Name}", name);
|
||||
return await _credentialService.DeleteCredentialAsync(name);
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Message)> TestSalesforceConnectionAsync(string credentialName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var credential = await GetSalesforceCredentialAsync(credentialName);
|
||||
if (credential == null)
|
||||
return (false, $"Credenziale Salesforce '{credentialName}' non trovata");
|
||||
|
||||
return await TestSalesforceConnectionAsync(credential);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel test della connessione Salesforce per credenziale: {Name}", credentialName);
|
||||
return (false, $"Errore nel test: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Message)> TestSalesforceConnectionAsync(SalesforceCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(credential.LoginUrl))
|
||||
return (false, "Login URL non specificato");
|
||||
|
||||
if (!Uri.TryCreate(credential.LoginUrl, UriKind.Absolute, out var uri))
|
||||
return (false, "Login URL non valido");
|
||||
|
||||
_logger.LogInformation("Testing Salesforce connection for {Name} ({LoginUrl})",
|
||||
credential.Name, credential.LoginUrl);
|
||||
|
||||
using var httpClient = new HttpClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(credential.TimeoutSeconds);
|
||||
|
||||
// Test di login SOAP/REST
|
||||
if (credential.UseSoapApi)
|
||||
{
|
||||
return await TestSalesforceSoapLogin(httpClient, credential);
|
||||
}
|
||||
else
|
||||
{
|
||||
return await TestSalesforceOAuthLogin(httpClient, credential);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel test della connessione Salesforce");
|
||||
return (false, $"Errore: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool Success, string Message)> TestSalesforceOAuthLogin(HttpClient httpClient, SalesforceCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tokenUrl = credential.LoginUrl.TrimEnd('/') + "/services/oauth2/token";
|
||||
|
||||
var tokenData = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new("grant_type", "password"),
|
||||
new("username", credential.Username),
|
||||
new("password", credential.Password + credential.SecurityToken),
|
||||
new("client_id", credential.ClientId ?? ""),
|
||||
new("client_secret", credential.ClientSecret ?? "")
|
||||
};
|
||||
|
||||
var tokenContent = new FormUrlEncodedContent(tokenData);
|
||||
var response = await httpClient.PostAsync(tokenUrl, tokenContent);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
return (true, $"Connessione Salesforce riuscita!\n\nDettagli:\n- Login URL: {credential.LoginUrl}\n- API Version: {credential.ApiVersion}\n- Sandbox: {credential.IsSandbox}\n- Tipo Auth: OAuth2\n- Timeout: {credential.TimeoutSeconds}s");
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
return (false, $"Login OAuth Salesforce fallito. Status: {response.StatusCode}\nDettagli: {errorContent}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, $"Errore OAuth Salesforce: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool Success, string Message)> TestSalesforceSoapLogin(HttpClient httpClient, SalesforceCredential credential)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Test semplificato per SOAP - verifica solo la raggiungibilità del servizio
|
||||
var soapUrl = credential.LoginUrl.TrimEnd('/') + "/services/Soap/u/" + credential.ApiVersion;
|
||||
|
||||
var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, soapUrl));
|
||||
|
||||
if (response.IsSuccessStatusCode || response.StatusCode == System.Net.HttpStatusCode.MethodNotAllowed)
|
||||
{
|
||||
return (true, $"Connessione Salesforce SOAP riuscita!\n\nDettagli:\n- SOAP URL: {soapUrl}\n- API Version: {credential.ApiVersion}\n- Sandbox: {credential.IsSandbox}\n- Tipo Auth: SOAP\n- Timeout: {credential.TimeoutSeconds}s");
|
||||
}
|
||||
else
|
||||
{
|
||||
return (false, $"Servizio SOAP Salesforce non raggiungibile. Status: {response.StatusCode}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, $"Errore SOAP Salesforce: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user