diff --git a/Components/ProfileSaver.razor.cs b/Components/ProfileSaver.razor.cs
index 2fd5a96..0622905 100644
--- a/Components/ProfileSaver.razor.cs
+++ b/Components/ProfileSaver.razor.cs
@@ -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,
diff --git a/CredentialManager/Migrations/20250705194127_AddSourceCustomQueryColumn.Designer.cs b/CredentialManager/Migrations/20250705194127_AddSourceCustomQueryColumn.Designer.cs
new file mode 100644
index 0000000..08f9357
--- /dev/null
+++ b/CredentialManager/Migrations/20250705194127_AddSourceCustomQueryColumn.Designer.cs
@@ -0,0 +1,341 @@
+//
+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
+ {
+ ///
+ 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("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AdditionalParameters")
+ .HasMaxLength(2000)
+ .HasColumnType("TEXT");
+
+ b.Property("CommandTimeout")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(30);
+
+ b.Property("ConnectionString")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedBy")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("DatabaseName")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("DatabaseType")
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("EncryptedApiKey")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("EncryptedAuthToken")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("EncryptedPassword")
+ .HasColumnType("TEXT");
+
+ b.Property("Headers")
+ .HasMaxLength(2000)
+ .HasColumnType("TEXT");
+
+ b.Property("Host")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("IgnoreSslErrors")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(false);
+
+ b.Property("IsActive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(true);
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Port")
+ .HasColumnType("INTEGER");
+
+ b.Property("RestServiceType")
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("TimeoutSeconds")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(100);
+
+ b.Property("Type")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("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("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP");
+
+ b.Property("CreatedBy")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("DestinationCredentialId")
+ .HasColumnType("INTEGER");
+
+ b.Property("DestinationEndpoint")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("DestinationSchema")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("DestinationTable")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("DestinationType")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("TEXT");
+
+ b.Property("FieldMappingJson")
+ .HasMaxLength(4000)
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(true);
+
+ b.Property("LastUsedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("SourceCredentialId")
+ .HasColumnType("INTEGER");
+
+ b.Property("SourceCustomQuery")
+ .HasMaxLength(2000)
+ .HasColumnType("TEXT");
+
+ b.Property("SourceDatabaseName")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("SourceFilePath")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("SourceKeyField")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("SourceSchema")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("SourceTable")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("SourceType")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("TEXT");
+
+ b.Property("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("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AdditionalInfo")
+ .HasMaxLength(2000)
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("DestinationEntity")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("DestinationId")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("DestinationKeyField")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER")
+ .HasDefaultValue(true);
+
+ b.Property("KeyValue")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("LastVerifiedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("RestCredentialName")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("SourceKeyField")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("SourcesInfo")
+ .HasMaxLength(2000)
+ .HasColumnType("TEXT");
+
+ b.Property("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
+ }
+ }
+}
diff --git a/CredentialManager/Migrations/20250705194127_AddSourceCustomQueryColumn.cs b/CredentialManager/Migrations/20250705194127_AddSourceCustomQueryColumn.cs
new file mode 100644
index 0000000..b4b97c2
--- /dev/null
+++ b/CredentialManager/Migrations/20250705194127_AddSourceCustomQueryColumn.cs
@@ -0,0 +1,29 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace CredentialManager.Migrations
+{
+ ///
+ public partial class AddSourceCustomQueryColumn : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "SourceCustomQuery",
+ table: "DataCouplerProfiles",
+ type: "TEXT",
+ maxLength: 2000,
+ nullable: true);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "SourceCustomQuery",
+ table: "DataCouplerProfiles");
+ }
+ }
+}
diff --git a/CredentialManager/Migrations/CredentialDbContextModelSnapshot.cs b/CredentialManager/Migrations/CredentialDbContextModelSnapshot.cs
index 60ff537..4f0b450 100644
--- a/CredentialManager/Migrations/CredentialDbContextModelSnapshot.cs
+++ b/CredentialManager/Migrations/CredentialDbContextModelSnapshot.cs
@@ -182,6 +182,10 @@ namespace CredentialManager.Migrations
b.Property("SourceCredentialId")
.HasColumnType("INTEGER");
+ b.Property("SourceCustomQuery")
+ .HasMaxLength(2000)
+ .HasColumnType("TEXT");
+
b.Property("SourceDatabaseName")
.HasMaxLength(200)
.HasColumnType("TEXT");
diff --git a/CredentialManager/Models/DataCouplerProfile.cs b/CredentialManager/Models/DataCouplerProfile.cs
index 9c4ab8d..5d015b4 100644
--- a/CredentialManager/Models/DataCouplerProfile.cs
+++ b/CredentialManager/Models/DataCouplerProfile.cs
@@ -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; }
diff --git a/CredentialManager/Models/DataCouplerProfileDto.cs b/CredentialManager/Models/DataCouplerProfileDto.cs
index a60a043..821a418 100644
--- a/CredentialManager/Models/DataCouplerProfileDto.cs
+++ b/CredentialManager/Models/DataCouplerProfileDto.cs
@@ -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
diff --git a/CredentialManager/Services/DataCouplerProfileService.cs b/CredentialManager/Services/DataCouplerProfileService.cs
index 7c23422..d940c16 100644
--- a/CredentialManager/Services/DataCouplerProfileService.cs
+++ b/CredentialManager/Services/DataCouplerProfileService.cs
@@ -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,
diff --git a/CredentialManager/design_time_temp.db b/CredentialManager/design_time_temp.db
index cf8a0be..02bade7 100644
Binary files a/CredentialManager/design_time_temp.db and b/CredentialManager/design_time_temp.db differ
diff --git a/Data_Coupler/Pages/DataCoupler.razor b/Data_Coupler/Pages/DataCoupler.razor
index 4f0cb22..4218126 100644
--- a/Data_Coupler/Pages/DataCoupler.razor
+++ b/Data_Coupler/Pages/DataCoupler.razor
@@ -1008,6 +1008,7 @@
SourceDatabaseName="@selectedDatabase"
SourceSchema="@GetCurrentDatabaseSchema()"
SourceTable="@(useCustomQuery ? "custom_query" : selectedTable)"
+ SourceCustomQuery="@(useCustomQuery ? customQuery : null)"
SourceFilePath="@selectedFileName"
DestinationType="rest"
DestinationCredentialId="@(GetCurrentDestinationCredentialIdAsync().Result)"
diff --git a/Data_Coupler/Pages/DataCoupler.razor.cs b/Data_Coupler/Pages/DataCoupler.razor.cs
index 6806bab..a1b34b3 100644
--- a/Data_Coupler/Pages/DataCoupler.razor.cs
+++ b/Data_Coupler/Pages/DataCoupler.razor.cs
@@ -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;
diff --git a/Data_Coupler/wwwroot/data/credentials.db b/Data_Coupler/wwwroot/data/credentials.db
index b1ecb44..59cc379 100644
Binary files a/Data_Coupler/wwwroot/data/credentials.db and b/Data_Coupler/wwwroot/data/credentials.db differ
diff --git a/Data_Coupler/wwwroot/data/credentials.db-shm b/Data_Coupler/wwwroot/data/credentials.db-shm
index 7f3662a..fe9ac28 100644
Binary files a/Data_Coupler/wwwroot/data/credentials.db-shm and b/Data_Coupler/wwwroot/data/credentials.db-shm differ
diff --git a/Data_Coupler/wwwroot/data/credentials.db-wal b/Data_Coupler/wwwroot/data/credentials.db-wal
index fd11c4d..e69de29 100644
Binary files a/Data_Coupler/wwwroot/data/credentials.db-wal and b/Data_Coupler/wwwroot/data/credentials.db-wal differ