Files
Data-Coupler/CredentialManager/Services/DataCouplerProfileService.cs
T
Alessio ee3c251b08 feat: Implementato supporto per query custom nei profili Data Coupler
- Aggiunto campo SourceCustomQuery al modello DataCouplerProfile e DTO
- Creata migrazione database per la nuova colonna SourceCustomQuery
- Aggiornato DataCouplerProfileService per gestire il mapping della query custom
- Modificato ProfileSaver per includere la query custom nel salvataggio
- Implementata logica di caricamento profili con supporto query custom:
  * Popolamento automatico della textbox con query salvata
  * Validazione ed esecuzione automatica della query al caricamento
  * Caricamento anteprima dati e mapping dopo validazione query
  * Gestione priorità: query custom ha precedenza sulla selezione tabella
- Aggiornato DataCoupler.razor per passare la query custom al ProfileSaver
- Corretto salvataggio profili esistenti per includere SourceCustomQuery

Il sistema ora permette di salvare e ripristinare completamente le configurazioni
con query SQL personalizzate, mantenendo il comportamento esistente per le
2025-07-05 21:56:13 +02:00

263 lines
9.0 KiB
C#

using Microsoft.EntityFrameworkCore;
using CredentialManager.Data;
using CredentialManager.Models;
using System.Text.Json;
namespace CredentialManager.Services;
/// <summary>
/// Implementazione del servizio per la gestione dei profili Data Coupler
/// </summary>
public class DataCouplerProfileService : IDataCouplerProfileService
{
private readonly CredentialDbContext _context;
public DataCouplerProfileService(CredentialDbContext context)
{
_context = context;
}
/// <summary>
/// Ottiene tutti i profili attivi
/// </summary>
public async Task<IEnumerable<DataCouplerProfile>> GetAllProfilesAsync()
{
return await _context.DataCouplerProfiles
.Include(p => p.SourceCredential)
.Include(p => p.DestinationCredential)
.Where(p => p.IsActive)
.OrderByDescending(p => p.LastUsedAt)
.ThenByDescending(p => p.CreatedAt)
.ToListAsync();
}
/// <summary>
/// Ottiene tutti i profili per nome (inclusi quelli inattivi)
/// </summary>
public async Task<DataCouplerProfile?> GetProfileByNameIncludingInactiveAsync(string name)
{
return await _context.DataCouplerProfiles
.Include(p => p.SourceCredential)
.Include(p => p.DestinationCredential)
.FirstOrDefaultAsync(p => p.Name.ToLower() == name.ToLower());
}
/// <summary>
/// Ottiene un profilo per ID
/// </summary>
public async Task<DataCouplerProfile?> GetProfileByIdAsync(int id)
{
return await _context.DataCouplerProfiles
.Include(p => p.SourceCredential)
.Include(p => p.DestinationCredential)
.FirstOrDefaultAsync(p => p.Id == id && p.IsActive);
}
/// <summary>
/// Ottiene un profilo per nome
/// </summary>
public async Task<DataCouplerProfile?> GetProfileByNameAsync(string name)
{
return await _context.DataCouplerProfiles
.Include(p => p.SourceCredential)
.Include(p => p.DestinationCredential)
.FirstOrDefaultAsync(p => p.Name == name && p.IsActive);
}
/// <summary>
/// Salva un nuovo profilo
/// </summary>
public async Task<DataCouplerProfile> SaveProfileAsync(DataCouplerProfile profile)
{
profile.CreatedAt = DateTime.UtcNow;
profile.IsActive = true;
_context.DataCouplerProfiles.Add(profile);
await _context.SaveChangesAsync();
return profile;
}
/// <summary>
/// Aggiorna un profilo esistente
/// </summary>
public async Task<DataCouplerProfile> UpdateProfileAsync(DataCouplerProfile profile)
{
var existingProfile = await _context.DataCouplerProfiles
.FirstOrDefaultAsync(p => p.Id == profile.Id);
if (existingProfile == null)
{
throw new InvalidOperationException($"Profilo con ID {profile.Id} non trovato");
}
// Aggiorna le proprietà (evita di aggiornare il nome se è uguale per evitare unique constraint)
if (!string.Equals(existingProfile.Name, profile.Name, StringComparison.OrdinalIgnoreCase))
{
existingProfile.Name = profile.Name;
}
existingProfile.Description = profile.Description;
existingProfile.SourceType = profile.SourceType;
existingProfile.SourceCredentialId = profile.SourceCredentialId;
existingProfile.SourceSchema = profile.SourceSchema;
existingProfile.SourceTable = profile.SourceTable;
existingProfile.SourceFilePath = profile.SourceFilePath;
existingProfile.DestinationType = profile.DestinationType;
existingProfile.DestinationCredentialId = profile.DestinationCredentialId;
existingProfile.DestinationSchema = profile.DestinationSchema;
existingProfile.DestinationTable = profile.DestinationTable;
existingProfile.DestinationEndpoint = profile.DestinationEndpoint;
existingProfile.FieldMappingJson = profile.FieldMappingJson;
existingProfile.SourceKeyField = profile.SourceKeyField;
existingProfile.UseRecordAssociations = profile.UseRecordAssociations;
existingProfile.IsActive = profile.IsActive;
await _context.SaveChangesAsync();
return existingProfile;
}
/// <summary>
/// Elimina un profilo (soft delete)
/// </summary>
public async Task<bool> DeleteProfileAsync(int id)
{
var profile = await _context.DataCouplerProfiles
.FirstOrDefaultAsync(p => p.Id == id);
if (profile == null)
{
return false;
}
profile.IsActive = false;
await _context.SaveChangesAsync();
return true;
}
/// <summary>
/// Aggiorna la data di ultimo utilizzo di un profilo
/// </summary>
public async Task UpdateLastUsedAsync(int id)
{
var profile = await _context.DataCouplerProfiles
.FirstOrDefaultAsync(p => p.Id == id);
if (profile != null)
{
profile.LastUsedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
}
}
/// <summary>
/// Verifica se esiste un profilo con il nome specificato
/// </summary>
public async Task<bool> ProfileExistsAsync(string name, int? excludeId = null)
{
var query = _context.DataCouplerProfiles
.Where(p => p.Name == name && p.IsActive);
if (excludeId.HasValue)
{
query = query.Where(p => p.Id != excludeId.Value);
}
return await query.AnyAsync();
}
/// <summary>
/// Serializza la lista di mapping dei campi in JSON
/// </summary>
public string SerializeFieldMappings(List<FieldMappingDto>? mappings)
{
if (mappings == null || !mappings.Any())
return string.Empty;
return JsonSerializer.Serialize(mappings, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
}
/// <summary>
/// Deserializza il JSON dei mapping dei campi
/// </summary>
public List<FieldMappingDto> DeserializeFieldMappings(string? json)
{
if (string.IsNullOrWhiteSpace(json))
return new List<FieldMappingDto>();
try
{
return JsonSerializer.Deserialize<List<FieldMappingDto>>(json, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}) ?? new List<FieldMappingDto>();
}
catch
{
return new List<FieldMappingDto>();
}
}
/// <summary>
/// Converte un DataCouplerProfile in DTO
/// </summary>
public DataCouplerProfileDto ToDto(DataCouplerProfile profile)
{
return new DataCouplerProfileDto
{
Id = profile.Id,
Name = profile.Name,
Description = profile.Description,
SourceType = profile.SourceType,
SourceCredentialId = profile.SourceCredentialId,
SourceCredentialName = profile.SourceCredential?.Name,
SourceDatabaseName = profile.SourceDatabaseName,
SourceSchema = profile.SourceSchema,
SourceTable = profile.SourceTable,
SourceCustomQuery = profile.SourceCustomQuery,
SourceFilePath = profile.SourceFilePath,
DestinationType = profile.DestinationType,
DestinationCredentialId = profile.DestinationCredentialId,
DestinationCredentialName = profile.DestinationCredential?.Name,
DestinationSchema = profile.DestinationSchema,
DestinationTable = profile.DestinationTable,
DestinationEndpoint = profile.DestinationEndpoint,
FieldMappings = DeserializeFieldMappings(profile.FieldMappingJson),
SourceKeyField = profile.SourceKeyField,
UseRecordAssociations = profile.UseRecordAssociations
};
}
/// <summary>
/// Converte un DTO in DataCouplerProfile
/// </summary>
public DataCouplerProfile FromDto(DataCouplerProfileDto dto, string? createdBy = null)
{
return new DataCouplerProfile
{
Id = dto.Id ?? 0,
Name = dto.Name,
Description = dto.Description,
SourceType = dto.SourceType,
SourceCredentialId = dto.SourceCredentialId,
SourceDatabaseName = dto.SourceDatabaseName,
SourceSchema = dto.SourceSchema,
SourceTable = dto.SourceTable,
SourceCustomQuery = dto.SourceCustomQuery,
SourceFilePath = dto.SourceFilePath,
DestinationType = dto.DestinationType,
DestinationCredentialId = dto.DestinationCredentialId,
DestinationSchema = dto.DestinationSchema,
DestinationTable = dto.DestinationTable,
DestinationEndpoint = dto.DestinationEndpoint,
FieldMappingJson = SerializeFieldMappings(dto.FieldMappings),
SourceKeyField = dto.SourceKeyField,
UseRecordAssociations = dto.UseRecordAssociations,
CreatedBy = createdBy
};
}
}