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