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
This commit is contained in:
@@ -16,6 +16,7 @@ public partial class ProfileSaver
|
||||
[Parameter] public string? SourceDatabaseName { get; set; }
|
||||
[Parameter] public string? SourceSchema { get; set; }
|
||||
[Parameter] public string? SourceTable { get; set; }
|
||||
[Parameter] public string? SourceCustomQuery { get; set; }
|
||||
[Parameter] public string? SourceFilePath { get; set; }
|
||||
[Parameter] public string DestinationType { get; set; } = "";
|
||||
[Parameter] public int? DestinationCredentialId { get; set; }
|
||||
@@ -68,6 +69,7 @@ public partial class ProfileSaver
|
||||
SourceDatabaseName = sourceDatabaseName,
|
||||
SourceSchema = SourceSchema,
|
||||
SourceTable = SourceTable,
|
||||
SourceCustomQuery = SourceCustomQuery,
|
||||
SourceFilePath = SourceFilePath,
|
||||
DestinationType = DestinationType,
|
||||
DestinationCredentialId = DestinationCredentialId,
|
||||
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CredentialManager.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CredentialManager.Migrations
|
||||
{
|
||||
[DbContext(typeof(CredentialDbContext))]
|
||||
[Migration("20250705194127_AddSourceCustomQueryColumn")]
|
||||
partial class AddSourceCustomQueryColumn
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.0");
|
||||
|
||||
modelBuilder.Entity("CredentialManager.Models.CredentialEntity", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AdditionalParameters")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("CommandTimeout")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(30);
|
||||
|
||||
b.Property<string>("ConnectionString")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DatabaseName")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DatabaseType")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EncryptedApiKey")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EncryptedAuthToken")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EncryptedPassword")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Headers")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Host")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IgnoreSslErrors")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("Port")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RestServiceType")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TimeoutSeconds")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(100);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DatabaseType");
|
||||
|
||||
b.HasIndex("IsActive");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Type");
|
||||
|
||||
b.ToTable("Credentials", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CredentialManager.Models.DataCouplerProfile", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("DestinationCredentialId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DestinationEndpoint")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DestinationSchema")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DestinationTable")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DestinationType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FieldMappingJson")
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<DateTime?>("LastUsedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("SourceCredentialId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SourceCustomQuery")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceDatabaseName")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceFilePath")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceKeyField")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceSchema")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceTable")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("UseRecordAssociations")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.HasIndex("DestinationCredentialId");
|
||||
|
||||
b.HasIndex("DestinationType");
|
||||
|
||||
b.HasIndex("IsActive");
|
||||
|
||||
b.HasIndex("LastUsedAt");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("SourceCredentialId");
|
||||
|
||||
b.HasIndex("SourceType");
|
||||
|
||||
b.ToTable("DataCouplerProfiles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CredentialManager.Models.KeyAssociation", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AdditionalInfo")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DestinationEntity")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DestinationId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DestinationKeyField")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<string>("KeyValue")
|
||||
.IsRequired()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("LastVerifiedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RestCredentialName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceKeyField")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourcesInfo")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.HasIndex("DestinationEntity");
|
||||
|
||||
b.HasIndex("IsActive");
|
||||
|
||||
b.HasIndex("KeyValue")
|
||||
.HasDatabaseName("IX_KeyAssociations_KeyValue");
|
||||
|
||||
b.HasIndex("LastVerifiedAt");
|
||||
|
||||
b.HasIndex("RestCredentialName");
|
||||
|
||||
b.HasIndex("KeyValue", "DestinationEntity", "RestCredentialName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("IX_KeyAssociations_Unique");
|
||||
|
||||
b.ToTable("KeyAssociations", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CredentialManager.Models.DataCouplerProfile", b =>
|
||||
{
|
||||
b.HasOne("CredentialManager.Models.CredentialEntity", "DestinationCredential")
|
||||
.WithMany()
|
||||
.HasForeignKey("DestinationCredentialId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CredentialManager.Models.CredentialEntity", "SourceCredential")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceCredentialId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("DestinationCredential");
|
||||
|
||||
b.Navigation("SourceCredential");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CredentialManager.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddSourceCustomQueryColumn : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SourceCustomQuery",
|
||||
table: "DataCouplerProfiles",
|
||||
type: "TEXT",
|
||||
maxLength: 2000,
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SourceCustomQuery",
|
||||
table: "DataCouplerProfiles");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,6 +182,10 @@ namespace CredentialManager.Migrations
|
||||
b.Property<int?>("SourceCredentialId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SourceCustomQuery")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceDatabaseName")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -34,6 +34,9 @@ public class DataCouplerProfile
|
||||
[MaxLength(200)]
|
||||
public string? SourceTable { get; set; }
|
||||
|
||||
[MaxLength(2000)]
|
||||
public string? SourceCustomQuery { get; set; }
|
||||
|
||||
[MaxLength(500)]
|
||||
public string? SourceFilePath { get; set; }
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ public class DataCouplerProfileDto
|
||||
public string? SourceDatabaseName { get; set; }
|
||||
public string? SourceSchema { get; set; }
|
||||
public string? SourceTable { get; set; }
|
||||
public string? SourceCustomQuery { get; set; }
|
||||
public string? SourceFilePath { get; set; }
|
||||
|
||||
// Informazioni destinazione
|
||||
|
||||
@@ -217,6 +217,7 @@ public class DataCouplerProfileService : IDataCouplerProfileService
|
||||
SourceDatabaseName = profile.SourceDatabaseName,
|
||||
SourceSchema = profile.SourceSchema,
|
||||
SourceTable = profile.SourceTable,
|
||||
SourceCustomQuery = profile.SourceCustomQuery,
|
||||
SourceFilePath = profile.SourceFilePath,
|
||||
DestinationType = profile.DestinationType,
|
||||
DestinationCredentialId = profile.DestinationCredentialId,
|
||||
@@ -245,6 +246,7 @@ public class DataCouplerProfileService : IDataCouplerProfileService
|
||||
SourceDatabaseName = dto.SourceDatabaseName,
|
||||
SourceSchema = dto.SourceSchema,
|
||||
SourceTable = dto.SourceTable,
|
||||
SourceCustomQuery = dto.SourceCustomQuery,
|
||||
SourceFilePath = dto.SourceFilePath,
|
||||
DestinationType = dto.DestinationType,
|
||||
DestinationCredentialId = dto.DestinationCredentialId,
|
||||
|
||||
Binary file not shown.
@@ -1008,6 +1008,7 @@
|
||||
SourceDatabaseName="@selectedDatabase"
|
||||
SourceSchema="@GetCurrentDatabaseSchema()"
|
||||
SourceTable="@(useCustomQuery ? "custom_query" : selectedTable)"
|
||||
SourceCustomQuery="@(useCustomQuery ? customQuery : null)"
|
||||
SourceFilePath="@selectedFileName"
|
||||
DestinationType="rest"
|
||||
DestinationCredentialId="@(GetCurrentDestinationCredentialIdAsync().Result)"
|
||||
|
||||
@@ -257,18 +257,48 @@ public partial class DataCoupler : ComponentBase
|
||||
Logger.LogInformation("Stato dopo connessione database - Connected: {Connected}, Tables: {TableCount}",
|
||||
isDatabaseConnected, availableTableNames.Count);
|
||||
|
||||
// Seleziona la tabella se specificata e se la connessione è riuscita
|
||||
if (!string.IsNullOrEmpty(profile.SourceTable) && isDatabaseConnected)
|
||||
// Gestisci la query custom se specificata nel profilo
|
||||
if (!string.IsNullOrEmpty(profile.SourceCustomQuery) && isDatabaseConnected)
|
||||
{
|
||||
Logger.LogInformation("Caricamento query custom dal profilo: {Query}", profile.SourceCustomQuery);
|
||||
|
||||
// Imposta la modalità query custom
|
||||
useCustomQuery = true;
|
||||
customQuery = profile.SourceCustomQuery;
|
||||
|
||||
// Valida ed esegui la query
|
||||
await ValidateCustomQuery();
|
||||
|
||||
if (isQueryValid)
|
||||
{
|
||||
Logger.LogInformation("Query custom caricata e validata con successo");
|
||||
|
||||
// Carica l'anteprima dei dati
|
||||
await LoadQueryPreview();
|
||||
|
||||
Logger.LogInformation("Anteprima dati della query custom caricata");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("La query custom dal profilo non è valida: {ValidationMessage}", queryValidationMessage);
|
||||
}
|
||||
}
|
||||
// Seleziona la tabella se specificata e se la connessione è riuscita (solo se non c'è una query custom)
|
||||
else if (!string.IsNullOrEmpty(profile.SourceTable) && isDatabaseConnected)
|
||||
{
|
||||
Logger.LogInformation("Selezione tabella: {Table}", profile.SourceTable);
|
||||
await SelectTable(profile.SourceTable);
|
||||
Logger.LogInformation("Tabella selezionata: {SelectedTable}, Schema caricato: {SchemaLoaded}",
|
||||
selectedTable, databaseTables.ContainsKey(profile.SourceTable));
|
||||
}
|
||||
else if (string.IsNullOrEmpty(profile.SourceCustomQuery) && string.IsNullOrEmpty(profile.SourceTable))
|
||||
{
|
||||
Logger.LogInformation("Nessuna tabella o query custom specificata nel profilo");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("Impossibile selezionare tabella - Table: {Table}, Connected: {Connected}",
|
||||
profile.SourceTable, isDatabaseConnected);
|
||||
Logger.LogWarning("Impossibile selezionare tabella o caricare query custom - Table: {Table}, Query: {Query}, Connected: {Connected}",
|
||||
profile.SourceTable, profile.SourceCustomQuery, isDatabaseConnected);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -454,6 +484,7 @@ public partial class DataCoupler : ComponentBase
|
||||
existingProfile.SourceCredentialId = profile.SourceCredentialId;
|
||||
existingProfile.SourceSchema = profile.SourceSchema;
|
||||
existingProfile.SourceTable = profile.SourceTable;
|
||||
existingProfile.SourceCustomQuery = profile.SourceCustomQuery;
|
||||
existingProfile.SourceFilePath = profile.SourceFilePath;
|
||||
existingProfile.DestinationType = profile.DestinationType;
|
||||
existingProfile.DestinationCredentialId = profile.DestinationCredentialId;
|
||||
@@ -486,6 +517,7 @@ public partial class DataCoupler : ComponentBase
|
||||
existingProfile.SourceCredentialId = profile.SourceCredentialId;
|
||||
existingProfile.SourceSchema = profile.SourceSchema;
|
||||
existingProfile.SourceTable = profile.SourceTable;
|
||||
existingProfile.SourceCustomQuery = profile.SourceCustomQuery;
|
||||
existingProfile.SourceFilePath = profile.SourceFilePath;
|
||||
existingProfile.DestinationType = profile.DestinationType;
|
||||
existingProfile.DestinationCredentialId = profile.DestinationCredentialId;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user