feat: Aggiunta gestione nome database sorgente nei profili DataCoupler
✨ Nuove funzionalità: - Aggiunto campo SourceDatabaseName nella tabella DataCouplerProfiles - Implementato recupero automatico del nome database dalle credenziali - Migliorata applicazione profili con supporto database specifico - Aggiornata logica di connessione database con selezione database 🔧 Modifiche tecniche: - Aggiunta migration per colonna SourceDatabaseName - Estesi modelli DataCouplerProfile e DataCouplerProfileDto - Aggiornato DataCouplerProfileService per gestire nuovo campo - Modificato ProfileSaver per recupero automatico database name - Implementato metodo ConnectToDatabaseWithSpecificDatabase 🐛 Correzioni: - Migliorata gestione connessioni database multi-database - Corretta formattazione codice e spaziature - Rimosse linee vuote eccessive nel codice sorgente 🧪
This commit is contained in:
@@ -44,6 +44,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Fonte -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Fonte</label>
|
||||||
|
<div class="bg-light p-3 rounded">
|
||||||
|
@* Contenuto esistente per la fonte *@
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Destinazione -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Destinazione</label>
|
||||||
|
<div class="bg-light p-3 rounded">
|
||||||
|
@* Contenuto esistente per la destinazione *@
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (!string.IsNullOrEmpty(SaveMessage))
|
@if (!string.IsNullOrEmpty(SaveMessage))
|
||||||
{
|
{
|
||||||
<div class="alert alert-@(SaveMessageType) mb-3">
|
<div class="alert alert-@(SaveMessageType) mb-3">
|
||||||
@@ -63,6 +79,10 @@
|
|||||||
{
|
{
|
||||||
<span class="text-muted">Credenziali: @SourceCredentialName</span><br />
|
<span class="text-muted">Credenziali: @SourceCredentialName</span><br />
|
||||||
}
|
}
|
||||||
|
@if (!string.IsNullOrEmpty(SourceDatabaseName))
|
||||||
|
{
|
||||||
|
<span class="text-muted">Database: @SourceDatabaseName <em>(dalla connessione attiva)</em></span><br />
|
||||||
|
}
|
||||||
@if (!string.IsNullOrEmpty(SourceSchema))
|
@if (!string.IsNullOrEmpty(SourceSchema))
|
||||||
{
|
{
|
||||||
<span class="text-muted">Schema: @SourceSchema</span><br />
|
<span class="text-muted">Schema: @SourceSchema</span><br />
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using CredentialManager.Models;
|
using CredentialManager.Models;
|
||||||
|
using CredentialManager.Services;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Components;
|
namespace Components;
|
||||||
|
|
||||||
public partial class ProfileSaver
|
public partial class ProfileSaver
|
||||||
{
|
{
|
||||||
|
[Inject] private ICredentialService CredentialService { get; set; } = default!;
|
||||||
|
|
||||||
[Parameter] public bool CanSave { get; set; }
|
[Parameter] public bool CanSave { get; set; }
|
||||||
[Parameter] public string SourceType { get; set; } = "";
|
[Parameter] public string SourceType { get; set; } = "";
|
||||||
[Parameter] public int? SourceCredentialId { get; set; }
|
[Parameter] public int? SourceCredentialId { get; set; }
|
||||||
[Parameter] public string? SourceCredentialName { get; set; }
|
[Parameter] public string? SourceCredentialName { get; set; }
|
||||||
|
[Parameter] public string? SourceDatabaseName { get; set; }
|
||||||
[Parameter] public string? SourceSchema { get; set; }
|
[Parameter] public string? SourceSchema { get; set; }
|
||||||
[Parameter] public string? SourceTable { get; set; }
|
[Parameter] public string? SourceTable { get; set; }
|
||||||
[Parameter] public string? SourceFilePath { get; set; }
|
[Parameter] public string? SourceFilePath { get; set; }
|
||||||
@@ -51,6 +55,9 @@ public partial class ProfileSaver
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Recupera automaticamente il nome del database dalla connessione attiva
|
||||||
|
var sourceDatabaseName = await GetSourceDatabaseNameAsync();
|
||||||
|
|
||||||
var profileDto = new DataCouplerProfileDto
|
var profileDto = new DataCouplerProfileDto
|
||||||
{
|
{
|
||||||
Name = ProfileData.Name,
|
Name = ProfileData.Name,
|
||||||
@@ -58,6 +65,7 @@ public partial class ProfileSaver
|
|||||||
SourceType = SourceType,
|
SourceType = SourceType,
|
||||||
SourceCredentialId = SourceCredentialId,
|
SourceCredentialId = SourceCredentialId,
|
||||||
SourceCredentialName = SourceCredentialName,
|
SourceCredentialName = SourceCredentialName,
|
||||||
|
SourceDatabaseName = sourceDatabaseName,
|
||||||
SourceSchema = SourceSchema,
|
SourceSchema = SourceSchema,
|
||||||
SourceTable = SourceTable,
|
SourceTable = SourceTable,
|
||||||
SourceFilePath = SourceFilePath,
|
SourceFilePath = SourceFilePath,
|
||||||
@@ -119,6 +127,34 @@ public partial class ProfileSaver
|
|||||||
SaveMessageType = type;
|
SaveMessageType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string?> GetSourceDatabaseNameAsync()
|
||||||
|
{
|
||||||
|
// Prima priorità: se SourceDatabaseName è già impostato come parametro, usa quello
|
||||||
|
if (!string.IsNullOrEmpty(SourceDatabaseName))
|
||||||
|
{
|
||||||
|
return SourceDatabaseName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seconda priorità: se abbiamo un SourceCredentialId, recupera il database dalle credenziali
|
||||||
|
if (SourceCredentialId.HasValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var credential = await CredentialService.GetDatabaseCredentialAsync(SourceCredentialId.Value);
|
||||||
|
if (credential != null && !string.IsNullOrEmpty(credential.DatabaseName))
|
||||||
|
{
|
||||||
|
return credential.DatabaseName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Se non riesce a recuperare le credenziali, continua con null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public class ProfileFormModel
|
public class ProfileFormModel
|
||||||
{
|
{
|
||||||
[Required(ErrorMessage = "Il nome del profilo è obbligatorio")]
|
[Required(ErrorMessage = "Il nome del profilo è obbligatorio")]
|
||||||
|
|||||||
+337
@@ -0,0 +1,337 @@
|
|||||||
|
// <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("20250704135720_AddSourceDatabaseNameColumn")]
|
||||||
|
partial class AddSourceDatabaseNameColumn
|
||||||
|
{
|
||||||
|
/// <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>("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 AddSourceDatabaseNameColumn : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "SourceDatabaseName",
|
||||||
|
table: "DataCouplerProfiles",
|
||||||
|
type: "TEXT",
|
||||||
|
maxLength: 200,
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "SourceDatabaseName",
|
||||||
|
table: "DataCouplerProfiles");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -182,6 +182,10 @@ namespace CredentialManager.Migrations
|
|||||||
b.Property<int?>("SourceCredentialId")
|
b.Property<int?>("SourceCredentialId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("SourceDatabaseName")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("SourceFilePath")
|
b.Property<string>("SourceFilePath")
|
||||||
.HasMaxLength(500)
|
.HasMaxLength(500)
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ public class DataCouplerProfile
|
|||||||
|
|
||||||
public int? SourceCredentialId { get; set; }
|
public int? SourceCredentialId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(200)]
|
||||||
|
public string? SourceDatabaseName { get; set; }
|
||||||
|
|
||||||
[MaxLength(200)]
|
[MaxLength(200)]
|
||||||
public string? SourceSchema { get; set; }
|
public string? SourceSchema { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public class DataCouplerProfileDto
|
|||||||
public string SourceType { get; set; } = string.Empty;
|
public string SourceType { get; set; } = string.Empty;
|
||||||
public int? SourceCredentialId { get; set; }
|
public int? SourceCredentialId { get; set; }
|
||||||
public string? SourceCredentialName { get; set; }
|
public string? SourceCredentialName { get; set; }
|
||||||
|
public string? SourceDatabaseName { get; set; }
|
||||||
public string? SourceSchema { get; set; }
|
public string? SourceSchema { get; set; }
|
||||||
public string? SourceTable { get; set; }
|
public string? SourceTable { get; set; }
|
||||||
public string? SourceFilePath { get; set; }
|
public string? SourceFilePath { get; set; }
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ public class DataCouplerProfileService : IDataCouplerProfileService
|
|||||||
SourceType = profile.SourceType,
|
SourceType = profile.SourceType,
|
||||||
SourceCredentialId = profile.SourceCredentialId,
|
SourceCredentialId = profile.SourceCredentialId,
|
||||||
SourceCredentialName = profile.SourceCredential?.Name,
|
SourceCredentialName = profile.SourceCredential?.Name,
|
||||||
|
SourceDatabaseName = profile.SourceDatabaseName,
|
||||||
SourceSchema = profile.SourceSchema,
|
SourceSchema = profile.SourceSchema,
|
||||||
SourceTable = profile.SourceTable,
|
SourceTable = profile.SourceTable,
|
||||||
SourceFilePath = profile.SourceFilePath,
|
SourceFilePath = profile.SourceFilePath,
|
||||||
@@ -241,6 +242,7 @@ public class DataCouplerProfileService : IDataCouplerProfileService
|
|||||||
Description = dto.Description,
|
Description = dto.Description,
|
||||||
SourceType = dto.SourceType,
|
SourceType = dto.SourceType,
|
||||||
SourceCredentialId = dto.SourceCredentialId,
|
SourceCredentialId = dto.SourceCredentialId,
|
||||||
|
SourceDatabaseName = dto.SourceDatabaseName,
|
||||||
SourceSchema = dto.SourceSchema,
|
SourceSchema = dto.SourceSchema,
|
||||||
SourceTable = dto.SourceTable,
|
SourceTable = dto.SourceTable,
|
||||||
SourceFilePath = dto.SourceFilePath,
|
SourceFilePath = dto.SourceFilePath,
|
||||||
|
|||||||
Binary file not shown.
@@ -1005,6 +1005,7 @@
|
|||||||
SourceType="@selectedSourceType"
|
SourceType="@selectedSourceType"
|
||||||
SourceCredentialId="@(GetCurrentSourceCredentialIdAsync().Result)"
|
SourceCredentialId="@(GetCurrentSourceCredentialIdAsync().Result)"
|
||||||
SourceCredentialName="@selectedDatabaseCredential"
|
SourceCredentialName="@selectedDatabaseCredential"
|
||||||
|
SourceDatabaseName="@selectedDatabase"
|
||||||
SourceSchema="@GetCurrentDatabaseSchema()"
|
SourceSchema="@GetCurrentDatabaseSchema()"
|
||||||
SourceTable="@(useCustomQuery ? "custom_query" : selectedTable)"
|
SourceTable="@(useCustomQuery ? "custom_query" : selectedTable)"
|
||||||
SourceFilePath="@selectedFileName"
|
SourceFilePath="@selectedFileName"
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public partial class DataCoupler : ComponentBase
|
|||||||
private RestEntitySummary? selectedRestEntity = null;
|
private RestEntitySummary? selectedRestEntity = null;
|
||||||
private RestEntityInfo? restEntityDetails = null;
|
private RestEntityInfo? restEntityDetails = null;
|
||||||
private string restSearchTerm = "";
|
private string restSearchTerm = "";
|
||||||
// Mapping campi
|
// Mapping campi
|
||||||
private Dictionary<string, string> fieldMappings = new(); // DbColumn -> RestProperty
|
private Dictionary<string, string> fieldMappings = new(); // DbColumn -> RestProperty
|
||||||
private HashSet<string> keyFields = new(); // REST properties marked as keys
|
private HashSet<string> keyFields = new(); // REST properties marked as keys
|
||||||
private string selectedDbColumn = "";
|
private string selectedDbColumn = "";
|
||||||
@@ -134,7 +134,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
await LoadCredentials();
|
await LoadCredentials();
|
||||||
} private async Task LoadCredentials()
|
}
|
||||||
|
private async Task LoadCredentials()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -235,14 +236,21 @@ public partial class DataCoupler : ComponentBase
|
|||||||
|
|
||||||
// Connetti al database
|
// Connetti al database
|
||||||
Logger.LogInformation("Iniziando connessione database...");
|
Logger.LogInformation("Iniziando connessione database...");
|
||||||
if (!string.IsNullOrEmpty(profile.SourceSchema))
|
|
||||||
|
// Gestione connessione con database specifico
|
||||||
|
if (!string.IsNullOrEmpty(profile.SourceDatabaseName))
|
||||||
|
{
|
||||||
|
Logger.LogInformation("Connessione con database specifico: {Database}", profile.SourceDatabaseName);
|
||||||
|
await ConnectToDatabaseWithSpecificDatabase(profile.SourceDatabaseName);
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(profile.SourceSchema))
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Connessione con schema specifico: {Schema}", profile.SourceSchema);
|
Logger.LogInformation("Connessione con schema specifico: {Schema}", profile.SourceSchema);
|
||||||
await ConnectToDatabaseWithSchema(profile.SourceSchema);
|
await ConnectToDatabaseWithSchema(profile.SourceSchema);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Connessione senza schema specifico");
|
Logger.LogInformation("Connessione senza database/schema specifico");
|
||||||
await ConnectToDatabase();
|
await ConnectToDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,7 +705,9 @@ public partial class DataCoupler : ComponentBase
|
|||||||
selectedSourceType = e.Value?.ToString() ?? "";
|
selectedSourceType = e.Value?.ToString() ?? "";
|
||||||
|
|
||||||
// Reset state when changing source type
|
// Reset state when changing source type
|
||||||
ResetSourceState(); } private void ResetSourceState()
|
ResetSourceState();
|
||||||
|
}
|
||||||
|
private void ResetSourceState()
|
||||||
{
|
{
|
||||||
// Reset database state
|
// Reset database state
|
||||||
ResetDatabaseState();
|
ResetDatabaseState();
|
||||||
@@ -718,7 +728,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnFileSelected(InputFileChangeEventArgs e)
|
private async Task OnFileSelected(InputFileChangeEventArgs e)
|
||||||
{ try
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
isProcessingFile = true;
|
isProcessingFile = true;
|
||||||
fileErrorMessage = "";
|
fileErrorMessage = "";
|
||||||
@@ -757,7 +768,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
isProcessingFile = false;
|
isProcessingFile = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
} private async Task ProcessCsvFile(IBrowserFile file)
|
}
|
||||||
|
private async Task ProcessCsvFile(IBrowserFile file)
|
||||||
{
|
{
|
||||||
using var stream = file.OpenReadStream(maxAllowedSize: 50 * 1024 * 1024); // Aumentato a 50MB
|
using var stream = file.OpenReadStream(maxAllowedSize: 50 * 1024 * 1024); // Aumentato a 50MB
|
||||||
using var reader = new StreamReader(stream);
|
using var reader = new StreamReader(stream);
|
||||||
@@ -794,7 +806,7 @@ public partial class DataCoupler : ComponentBase
|
|||||||
|
|
||||||
var values = ParseCsvLine(line, separator);
|
var values = ParseCsvLine(line, separator);
|
||||||
var row = new Dictionary<string, object>();
|
var row = new Dictionary<string, object>();
|
||||||
for (int i = 0; i < headers.Count; i++)
|
for (int i = 0; i < headers.Count; i++)
|
||||||
{
|
{
|
||||||
var value = i < values.Count ? values[i] : "";
|
var value = i < values.Count ? values[i] : "";
|
||||||
row[headers[i]] = string.IsNullOrEmpty(value) ? "" : value;
|
row[headers[i]] = string.IsNullOrEmpty(value) ? "" : value;
|
||||||
@@ -809,14 +821,15 @@ public partial class DataCoupler : ComponentBase
|
|||||||
Logger.LogInformation("CSV row {RowNumber}: {Values}", rowNumber - 1, string.Join(" | ", values));
|
Logger.LogInformation("CSV row {RowNumber}: {Values}", rowNumber - 1, string.Join(" | ", values));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fileData[sheetName] = dataRows;
|
fileData[sheetName] = dataRows;
|
||||||
|
|
||||||
// Auto-seleziona il foglio per i CSV dato che ce n'è solo uno
|
// Auto-seleziona il foglio per i CSV dato che ce n'è solo uno
|
||||||
selectedSheet = sheetName;
|
selectedSheet = sheetName;
|
||||||
|
|
||||||
Logger.LogInformation("CSV file processed: {FileName}, Headers: {HeaderCount} ({Headers}), Rows: {RowCount}, Auto-selected sheet: {SheetName}",
|
Logger.LogInformation("CSV file processed: {FileName}, Headers: {HeaderCount} ({Headers}), Rows: {RowCount}, Auto-selected sheet: {SheetName}",
|
||||||
file.Name, headers.Count, string.Join(", ", headers), dataRows.Count, selectedSheet);
|
file.Name, headers.Count, string.Join(", ", headers), dataRows.Count, selectedSheet);
|
||||||
} private List<string> ParseCsvLine(string line, char separator = ',')
|
}
|
||||||
|
private List<string> ParseCsvLine(string line, char separator = ',')
|
||||||
{
|
{
|
||||||
var result = new List<string>();
|
var result = new List<string>();
|
||||||
var current = new StringBuilder();
|
var current = new StringBuilder();
|
||||||
@@ -856,7 +869,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
result.Add(current.ToString().Trim());
|
result.Add(current.ToString().Trim());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}private async Task ProcessExcelFile(IBrowserFile file)
|
}
|
||||||
|
private async Task ProcessExcelFile(IBrowserFile file)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -948,7 +962,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
{
|
{
|
||||||
selectedSheet = fileSheets.First().Key;
|
selectedSheet = fileSheets.First().Key;
|
||||||
Logger.LogInformation("Auto-selected first sheet: {SheetName}", selectedSheet);
|
Logger.LogInformation("Auto-selected first sheet: {SheetName}", selectedSheet);
|
||||||
} Logger.LogInformation("Excel file processing completed: {FileName}, Total sheets: {SheetCount}, Selected: {SelectedSheet}",
|
}
|
||||||
|
Logger.LogInformation("Excel file processing completed: {FileName}, Total sheets: {SheetCount}, Selected: {SelectedSheet}",
|
||||||
file.Name, fileSheets.Count, selectedSheet);
|
file.Name, fileSheets.Count, selectedSheet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -959,7 +974,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
} private void SelectSheet(string sheetName)
|
}
|
||||||
|
private void SelectSheet(string sheetName)
|
||||||
{
|
{
|
||||||
selectedSheet = sheetName;
|
selectedSheet = sheetName;
|
||||||
|
|
||||||
@@ -1018,7 +1034,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
if (string.IsNullOrEmpty(selectedSheet) || !fileData.ContainsKey(selectedSheet))
|
if (string.IsNullOrEmpty(selectedSheet) || !fileData.ContainsKey(selectedSheet))
|
||||||
return 0;
|
return 0;
|
||||||
return (currentPage - 1) * pageSize + 1;
|
return (currentPage - 1) * pageSize + 1;
|
||||||
} private int GetEndRecord()
|
}
|
||||||
|
private int GetEndRecord()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(selectedSheet) || !fileData.ContainsKey(selectedSheet))
|
if (string.IsNullOrEmpty(selectedSheet) || !fileData.ContainsKey(selectedSheet))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1035,11 +1052,13 @@ public partial class DataCoupler : ComponentBase
|
|||||||
currentPage = 1; // Reset to first page when changing page size
|
currentPage = 1; // Reset to first page when changing page size
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}private void OnDatabaseCredentialChanged(ChangeEventArgs e)
|
}
|
||||||
|
private void OnDatabaseCredentialChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
selectedDatabaseCredential = e.Value?.ToString() ?? "";
|
selectedDatabaseCredential = e.Value?.ToString() ?? "";
|
||||||
ResetDatabaseState();
|
ResetDatabaseState();
|
||||||
} private void OnRestCredentialChanged(ChangeEventArgs e)
|
}
|
||||||
|
private void OnRestCredentialChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
var newCredential = e.Value?.ToString() ?? "";
|
var newCredential = e.Value?.ToString() ?? "";
|
||||||
|
|
||||||
@@ -1052,7 +1071,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
|
|
||||||
selectedRestCredential = newCredential;
|
selectedRestCredential = newCredential;
|
||||||
ResetRestState();
|
ResetRestState();
|
||||||
} private void ResetDatabaseState()
|
}
|
||||||
|
private void ResetDatabaseState()
|
||||||
{
|
{
|
||||||
isDatabaseConnected = false;
|
isDatabaseConnected = false;
|
||||||
databaseTables.Clear();
|
databaseTables.Clear();
|
||||||
@@ -1082,7 +1102,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
|
|
||||||
// Clear mappings when resetting database state
|
// Clear mappings when resetting database state
|
||||||
ClearAllMappings();
|
ClearAllMappings();
|
||||||
} private void ResetRestState()
|
}
|
||||||
|
private void ResetRestState()
|
||||||
{
|
{
|
||||||
isRestConnected = false;
|
isRestConnected = false;
|
||||||
restEntities.Clear();
|
restEntities.Clear();
|
||||||
@@ -1095,7 +1116,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
|
|
||||||
// Clear mappings when resetting REST state
|
// Clear mappings when resetting REST state
|
||||||
ClearAllMappings();
|
ClearAllMappings();
|
||||||
} private async Task ConnectToDatabase()
|
}
|
||||||
|
private async Task ConnectToDatabase()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(selectedDatabaseCredential))
|
if (string.IsNullOrEmpty(selectedDatabaseCredential))
|
||||||
return;
|
return;
|
||||||
@@ -1175,7 +1197,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
isConnectingDatabase = false;
|
isConnectingDatabase = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}private async Task ConnectToRestApi()
|
}
|
||||||
|
private async Task ConnectToRestApi()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(selectedRestCredential))
|
if (string.IsNullOrEmpty(selectedRestCredential))
|
||||||
return;
|
return;
|
||||||
@@ -1201,7 +1224,7 @@ public partial class DataCoupler : ComponentBase
|
|||||||
return;
|
return;
|
||||||
} // Crea i client REST usando il factory con le credenziali complete
|
} // Crea i client REST usando il factory con le credenziali complete
|
||||||
currentRestClient = await ConnectionFactory.CreateRestServiceClientAsync(selectedRestCredential);
|
currentRestClient = await ConnectionFactory.CreateRestServiceClientAsync(selectedRestCredential);
|
||||||
currentRestDiscovery = await ConnectionFactory.CreateRestMetadataDiscoveryAsync(selectedRestCredential); Logger.LogInformation("Iniziando autenticazione per il servizio REST {ServiceType} con credenziale: {CredentialName}", credential.ServiceType, selectedRestCredential);
|
currentRestDiscovery = await ConnectionFactory.CreateRestMetadataDiscoveryAsync(selectedRestCredential); Logger.LogInformation("Iniziando autenticazione per il servizio REST {ServiceType} con credenziale: {CredentialName}", credential.ServiceType, selectedRestCredential);
|
||||||
|
|
||||||
// Autenticazione prima del discovery
|
// Autenticazione prima del discovery
|
||||||
var authResult = await currentRestClient.AuthenticateAsync();
|
var authResult = await currentRestClient.AuthenticateAsync();
|
||||||
@@ -1237,7 +1260,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
{
|
{
|
||||||
isConnectingRest = false;
|
isConnectingRest = false;
|
||||||
}
|
}
|
||||||
} private async Task SelectTable(string tableName)
|
}
|
||||||
|
private async Task SelectTable(string tableName)
|
||||||
{
|
{
|
||||||
selectedTable = tableName;
|
selectedTable = tableName;
|
||||||
|
|
||||||
@@ -1326,7 +1350,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
if (currentRestDiscovery != null)
|
if (currentRestDiscovery != null)
|
||||||
{
|
{
|
||||||
// Discovery dei dettagli dell'entità
|
// Discovery dei dettagli dell'entità
|
||||||
restEntityDetails = await currentRestDiscovery.DiscoverEntityDetailsAsync(entity.Name); }
|
restEntityDetails = await currentRestDiscovery.DiscoverEntityDetailsAsync(entity.Name);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
restErrorMessage = "Servizio di discovery REST non disponibile";
|
restErrorMessage = "Servizio di discovery REST non disponibile";
|
||||||
@@ -1337,7 +1362,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
{
|
{
|
||||||
Logger.LogError(ex, "Errore nel caricamento dettagli entità {EntityName}", entity.Name);
|
Logger.LogError(ex, "Errore nel caricamento dettagli entità {EntityName}", entity.Name);
|
||||||
restErrorMessage = $"Errore nel caricamento dettagli entità: {ex.Message}";
|
restErrorMessage = $"Errore nel caricamento dettagli entità: {ex.Message}";
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Metodi per la ricerca e il filtraggio
|
// Metodi per la ricerca e il filtraggio
|
||||||
private IEnumerable<string> GetFilteredDatabaseTables()
|
private IEnumerable<string> GetFilteredDatabaseTables()
|
||||||
@@ -1422,7 +1448,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
|
|
||||||
fieldMappings.Remove(selectedDbColumn);
|
fieldMappings.Remove(selectedDbColumn);
|
||||||
Logger.LogInformation("Rimosso mapping per campo: {DbColumn}", selectedDbColumn);
|
Logger.LogInformation("Rimosso mapping per campo: {DbColumn}", selectedDbColumn);
|
||||||
} private void RemoveSpecificMapping(string dbColumn)
|
}
|
||||||
|
private void RemoveSpecificMapping(string dbColumn)
|
||||||
{
|
{
|
||||||
if (fieldMappings.ContainsKey(dbColumn))
|
if (fieldMappings.ContainsKey(dbColumn))
|
||||||
{
|
{
|
||||||
@@ -1490,7 +1517,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
|
|
||||||
Logger.LogInformation("Auto-mapping completato. Creati {Count} mapping automatici da {SourceType}",
|
Logger.LogInformation("Auto-mapping completato. Creati {Count} mapping automatici da {SourceType}",
|
||||||
mappingsCreated, useCustomQuery ? "query custom" : selectedSourceType);
|
mappingsCreated, useCustomQuery ? "query custom" : selectedSourceType);
|
||||||
} private async Task ShowMappingSummary()
|
}
|
||||||
|
private async Task ShowMappingSummary()
|
||||||
{
|
{
|
||||||
var summary = "Riepilogo Configurazione:\n\n";
|
var summary = "Riepilogo Configurazione:\n\n";
|
||||||
summary += "=== MAPPING CAMPI ===\n";
|
summary += "=== MAPPING CAMPI ===\n";
|
||||||
@@ -1507,7 +1535,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
await JSRuntime.InvokeVoidAsync("alert", summary);
|
await JSRuntime.InvokeVoidAsync("alert", summary);
|
||||||
} private async Task StartDataTransfer()
|
}
|
||||||
|
private async Task StartDataTransfer()
|
||||||
{
|
{
|
||||||
if (!fieldMappings.Any() || currentRestClient == null || selectedRestEntity == null)
|
if (!fieldMappings.Any() || currentRestClient == null || selectedRestEntity == null)
|
||||||
{
|
{
|
||||||
@@ -1697,7 +1726,7 @@ public partial class DataCoupler : ComponentBase
|
|||||||
goto HandleInvalidAssociation;
|
goto HandleInvalidAssociation;
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleInvalidAssociation:
|
HandleInvalidAssociation:
|
||||||
// L'ID di destinazione non esiste più o l'update è fallito - elimina l'associazione non valida
|
// L'ID di destinazione non esiste più o l'update è fallito - elimina l'associazione non valida
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -1837,7 +1866,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
{
|
{
|
||||||
isTransferringData = false;
|
isTransferringData = false;
|
||||||
}
|
}
|
||||||
} private async Task<IEnumerable<Dictionary<string, object>>> GetAllRecordsFromSource()
|
}
|
||||||
|
private async Task<IEnumerable<Dictionary<string, object>>> GetAllRecordsFromSource()
|
||||||
{
|
{
|
||||||
if (selectedSourceType == "database")
|
if (selectedSourceType == "database")
|
||||||
{
|
{
|
||||||
@@ -1894,7 +1924,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
useCustomQuery, selectedTable, useCustomQuery ? customQuery : "N/A");
|
useCustomQuery, selectedTable, useCustomQuery ? customQuery : "N/A");
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
} private async Task<IEnumerable<Dictionary<string, object>>> GetAllRecordsFromFile()
|
}
|
||||||
|
private async Task<IEnumerable<Dictionary<string, object>>> GetAllRecordsFromFile()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(selectedSheet) || !fileData.ContainsKey(selectedSheet))
|
if (string.IsNullOrEmpty(selectedSheet) || !fileData.ContainsKey(selectedSheet))
|
||||||
{
|
{
|
||||||
@@ -2220,8 +2251,8 @@ public partial class DataCoupler : ComponentBase
|
|||||||
if (databaseTables.Count == 0)
|
if (databaseTables.Count == 0)
|
||||||
{
|
{
|
||||||
// Se non ci sono tabelle, potrebbe essere necessario selezionare un database specifico
|
// Se non ci sono tabelle, potrebbe essere necessario selezionare un database specifico
|
||||||
// Schema discovery completato senza successo
|
// Schema discovery completato senza successo
|
||||||
databaseErrorMessage = "Impossibile rilevare le tabelle del database. Verificare le credenziali di connessione.";
|
databaseErrorMessage = "Impossibile rilevare le tabelle del database. Verificare le credenziali di connessione.";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2549,7 +2580,7 @@ public partial class DataCoupler : ComponentBase
|
|||||||
{
|
{
|
||||||
return databaseType switch
|
return databaseType switch
|
||||||
{
|
{
|
||||||
DatabaseType.SqlServer => $"SELECT TOP {limit} * FROM ({baseQuery}) AS subquery",
|
DatabaseType.SqlServer => $"SELECT TOP {limit} * FROM ({baseQuery}) AS subquery",
|
||||||
DatabaseType.Oracle => $"SELECT * FROM ({baseQuery}) WHERE ROWNUM <= {limit}",
|
DatabaseType.Oracle => $"SELECT * FROM ({baseQuery}) WHERE ROWNUM <= {limit}",
|
||||||
DatabaseType.MySql => $"{baseQuery} LIMIT {limit}",
|
DatabaseType.MySql => $"{baseQuery} LIMIT {limit}",
|
||||||
DatabaseType.PostgreSql => $"{baseQuery} LIMIT {limit}",
|
DatabaseType.PostgreSql => $"{baseQuery} LIMIT {limit}",
|
||||||
@@ -3066,8 +3097,34 @@ public partial class DataCoupler : ComponentBase
|
|||||||
|
|
||||||
private async Task ConnectToDatabaseWithSpecificDatabase(string databaseName)
|
private async Task ConnectToDatabaseWithSpecificDatabase(string databaseName)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(selectedDatabaseCredential))
|
||||||
|
return;
|
||||||
|
|
||||||
|
isConnectingDatabase = true;
|
||||||
|
databaseErrorMessage = "";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Trova la credenziale
|
||||||
|
var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential);
|
||||||
|
if (credential == null)
|
||||||
|
{
|
||||||
|
databaseErrorMessage = "Credenziale database non trovata";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test della connessione
|
||||||
|
var (success, message) = await CredentialService.TestDatabaseConnectionAsync(credential.Name);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
databaseErrorMessage = $"Connessione fallita: {message}";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crea il database manager
|
||||||
|
Logger.LogInformation("Creando database manager per credenziale: {CredentialName}", selectedDatabaseCredential);
|
||||||
|
currentDatabaseManager = await ConnectionFactory.CreateDatabaseManagerAsync(selectedDatabaseCredential);
|
||||||
|
Logger.LogInformation("Database manager creato con successo");
|
||||||
if (currentDatabaseManager == null)
|
if (currentDatabaseManager == null)
|
||||||
{
|
{
|
||||||
databaseErrorMessage = "Database manager non disponibile";
|
databaseErrorMessage = "Database manager non disponibile";
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,53 @@
|
|||||||
|
using CredentialManager.Data;
|
||||||
|
using CredentialManager.Models;
|
||||||
|
using CredentialManager.Services;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
Console.WriteLine("🧪 Testing SourceDatabaseName retrieval from credentials...");
|
||||||
|
|
||||||
|
// Configurazione del database temporaneo
|
||||||
|
var options = new DbContextOptionsBuilder<CredentialDbContext>()
|
||||||
|
.UseSqlite("Data Source=test_credential_db.db")
|
||||||
|
.Options;
|
||||||
|
|
||||||
|
using var context = new CredentialDbContext(options);
|
||||||
|
await context.Database.EnsureCreatedAsync();
|
||||||
|
|
||||||
|
var credentialService = new CredentialService(context);
|
||||||
|
|
||||||
|
// Test 1: Crea una credenziale database con nome database
|
||||||
|
var testCredential = new DatabaseCredential
|
||||||
|
{
|
||||||
|
Name = "TestDatabaseCredential",
|
||||||
|
DatabaseType = "SqlServer",
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 1433,
|
||||||
|
DatabaseName = "MyProductionDB",
|
||||||
|
Username = "testuser",
|
||||||
|
Password = "testpassword"
|
||||||
|
};
|
||||||
|
|
||||||
|
Console.WriteLine($"📝 Creando credenziale con DatabaseName: {testCredential.DatabaseName}");
|
||||||
|
var credentialId = await credentialService.SaveDatabaseCredentialAsync(testCredential);
|
||||||
|
Console.WriteLine($"✅ Credenziale salvata con ID: {credentialId}");
|
||||||
|
|
||||||
|
// Test 2: Recupera la credenziale
|
||||||
|
var retrievedCredential = await credentialService.GetDatabaseCredentialAsync(credentialId);
|
||||||
|
Console.WriteLine($"✅ Credenziale recuperata: {retrievedCredential?.Name}");
|
||||||
|
Console.WriteLine($" DatabaseName: {retrievedCredential?.DatabaseName}");
|
||||||
|
|
||||||
|
// Test 3: Simula il recupero del database name come farebbe ProfileSaver
|
||||||
|
if (retrievedCredential != null && !string.IsNullOrEmpty(retrievedCredential.DatabaseName))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"✅ SUCCESSO: DatabaseName recuperato dalle credenziali: {retrievedCredential.DatabaseName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("❌ ERRORE: DatabaseName non recuperato dalle credenziali");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pulizia
|
||||||
|
await context.Database.EnsureDeletedAsync();
|
||||||
|
Console.WriteLine("🧹 Database temporaneo eliminato");
|
||||||
|
|
||||||
|
Console.WriteLine("\n🎯 Test completato con successo!");
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\CredentialManager\CredentialManager.csproj" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using CredentialManager.Data;
|
||||||
|
using CredentialManager.Models;
|
||||||
|
using CredentialManager.Services;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
Console.WriteLine("🧪 Testing SourceDatabaseName database persistence...");
|
||||||
|
|
||||||
|
// Configurazione del database temporaneo
|
||||||
|
var options = new DbContextOptionsBuilder<CredentialDbContext>()
|
||||||
|
.UseSqlite("Data Source=test_sourcedatabase.db")
|
||||||
|
.Options;
|
||||||
|
|
||||||
|
using var context = new CredentialDbContext(options);
|
||||||
|
await context.Database.EnsureCreatedAsync();
|
||||||
|
|
||||||
|
var profileService = new DataCouplerProfileService(context);
|
||||||
|
|
||||||
|
// Test: Creazione e salvataggio di un profilo con SourceDatabaseName
|
||||||
|
var testProfile = new DataCouplerProfile
|
||||||
|
{
|
||||||
|
Name = "Test Profile DB",
|
||||||
|
Description = "Test per verificare il salvataggio del SourceDatabaseName",
|
||||||
|
SourceType = "database",
|
||||||
|
SourceDatabaseName = "MyProductionDatabase",
|
||||||
|
SourceSchema = "dbo",
|
||||||
|
SourceTable = "customers",
|
||||||
|
DestinationType = "rest",
|
||||||
|
DestinationEndpoint = "/api/customers",
|
||||||
|
CreatedBy = "TestUser"
|
||||||
|
};
|
||||||
|
|
||||||
|
Console.WriteLine($"📝 Creando profilo con SourceDatabaseName: {testProfile.SourceDatabaseName}");
|
||||||
|
|
||||||
|
// Salvataggio nel database
|
||||||
|
var savedProfile = await profileService.SaveProfileAsync(testProfile);
|
||||||
|
Console.WriteLine($"✅ Profilo salvato con ID: {savedProfile.Id}");
|
||||||
|
|
||||||
|
// Recupero dal database
|
||||||
|
var retrievedProfile = await profileService.GetProfileByIdAsync(savedProfile.Id);
|
||||||
|
Console.WriteLine($"✅ Profilo recuperato dal database");
|
||||||
|
|
||||||
|
// Verifica che il SourceDatabaseName sia stato salvato e recuperato correttamente
|
||||||
|
if (retrievedProfile != null && retrievedProfile.SourceDatabaseName == testProfile.SourceDatabaseName)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"✅ SUCCESSO: SourceDatabaseName salvato e recuperato correttamente: {retrievedProfile.SourceDatabaseName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"❌ ERRORE: SourceDatabaseName non salvato correttamente");
|
||||||
|
Console.WriteLine($" Originale: {testProfile.SourceDatabaseName}");
|
||||||
|
Console.WriteLine($" Recuperato: {retrievedProfile?.SourceDatabaseName ?? "NULL"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test conversione DTO
|
||||||
|
var dto = profileService.ToDto(retrievedProfile!);
|
||||||
|
Console.WriteLine($"✅ DTO convertito con SourceDatabaseName: {dto.SourceDatabaseName}");
|
||||||
|
|
||||||
|
// Pulizia
|
||||||
|
await context.Database.EnsureDeletedAsync();
|
||||||
|
Console.WriteLine("🧹 Database temporaneo eliminato");
|
||||||
|
|
||||||
|
Console.WriteLine("\n🎯 Test completato con successo!");
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\CredentialManager\CredentialManager.csproj" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user