[Feature] Implementato sistema di valori default per campi mapping
- Creato modello FieldMappingEntry per gestione unificata di field mapping e default values - Aggiunta colonna DefaultValuesJson alla tabella DataCouplerProfile (max 4000 caratteri) - Implementata UI con toggle per selezionare modalità Mapping o Default - Supporto per 9 tipi di dati: string, int, long, decimal, double, float, boolean, datetime, datetimeoffset - Aggiornata logica TransformRecordToRestEntity per applicare valori default dopo field mapping - Implementata serializzazione/deserializzazione DefaultValues in DataCouplerProfileService - Sistema completo di salvataggio/caricamento valori default nei profili - Migrazione database AddDefaultValuesJsonToProfile creata e applicata
This commit is contained in:
Generated
+601
@@ -0,0 +1,601 @@
|
||||
// <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.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(CredentialDbContext))]
|
||||
[Migration("20260216113009_AddDefaultValuesJsonToProfile")]
|
||||
partial class AddDefaultValuesJsonToProfile
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.6");
|
||||
|
||||
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<string>("OdbcDsnName")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OdbcMode")
|
||||
.HasMaxLength(20)
|
||||
.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>("DefaultValuesJson")
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DeletionAction")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DeletionMarkField")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DeletionMarkValue")
|
||||
.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>("ExternalIdRelationshipsJson")
|
||||
.HasMaxLength(4000)
|
||||
.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>("SyncDeletions")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
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>("Data_Hash")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("DeletionSynced")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("DeletionSyncedAt")
|
||||
.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<bool>("IsSourceDeleted")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("KeyValue")
|
||||
.IsRequired()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("LastVerifiedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MappedDestinationField")
|
||||
.HasMaxLength(200)
|
||||
.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.ProfileSchedule", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DailyTime")
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("DayOfMonth")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("DayOfWeek")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DestinationDatabaseOverride")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("EnableDeletionSync")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ExecutionCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("IntervalUnit")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("IntervalValue")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LastExecutionMessage")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("LastExecutionRecordCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LastExecutionStatus")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("LastExecutionTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("NextExecutionTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ProfileId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ScheduleType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("ScheduledDateTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceDatabaseOverride")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ProfileId");
|
||||
|
||||
b.ToTable("ProfileSchedules");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CredentialManager.Models.ScheduleExecutionHistory", 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>("DestinationInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DestinationType")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("EndTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ErrorDetails")
|
||||
.HasMaxLength(5000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.HasMaxLength(2000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ProfileId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ProfileName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("RecordsProcessed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("RecordsWithErrors")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SourceInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SourceType")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("StartTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TriggerType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TriggeredBy")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ProfileId");
|
||||
|
||||
b.HasIndex("ScheduleId");
|
||||
|
||||
b.HasIndex("StartTime");
|
||||
|
||||
b.HasIndex("Status");
|
||||
|
||||
b.HasIndex("TriggerType");
|
||||
|
||||
b.ToTable("ScheduleExecutionHistories", (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");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CredentialManager.Models.ProfileSchedule", b =>
|
||||
{
|
||||
b.HasOne("CredentialManager.Models.DataCouplerProfile", "Profile")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CredentialManager.Models.ScheduleExecutionHistory", b =>
|
||||
{
|
||||
b.HasOne("CredentialManager.Models.ProfileSchedule", "Schedule")
|
||||
.WithMany()
|
||||
.HasForeignKey("ScheduleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Schedule");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CredentialManager.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDefaultValuesJsonToProfile : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "DefaultValuesJson",
|
||||
table: "DataCouplerProfiles",
|
||||
type: "TEXT",
|
||||
maxLength: 4000,
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DefaultValuesJson",
|
||||
table: "DataCouplerProfiles");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,6 +146,10 @@ namespace CredentialManager.Migrations
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DefaultValuesJson")
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DeletionAction")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -60,6 +60,11 @@ public class DataCouplerProfile
|
||||
[MaxLength(4000)]
|
||||
public string? FieldMappingJson { get; set; }
|
||||
|
||||
// Default values per i campi di destinazione salvati come JSON
|
||||
// Formato: { "DestinationField": { "Value": "defaultValue", "Type": "string" } }
|
||||
[MaxLength(4000)]
|
||||
public string? DefaultValuesJson { get; set; }
|
||||
|
||||
// External ID Relationships per Salesforce salvate come JSON
|
||||
[MaxLength(4000)]
|
||||
public string? ExternalIdRelationshipsJson { get; set; }
|
||||
|
||||
@@ -30,6 +30,9 @@ public class DataCouplerProfileDto
|
||||
// Mapping dei campi
|
||||
public List<FieldMappingDto>? FieldMappings { get; set; }
|
||||
|
||||
// Default values per campi destinazione (FieldName -> (Value, Type))
|
||||
public Dictionary<string, (object? Value, string? Type)>? DefaultValues { get; set; }
|
||||
|
||||
// External ID Relationships per Salesforce
|
||||
public List<ExternalIdRelationshipDto>? ExternalIdRelationships { get; set; }
|
||||
|
||||
@@ -83,8 +86,15 @@ public class ExternalIdRelationshipDto
|
||||
public string SourceField { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DTO per la visualizzazione di un profilo nella lista
|
||||
/// <summary>/// DTO per i valori di default
|
||||
/// </summary>
|
||||
public class DefaultValueDto
|
||||
{
|
||||
public object? Value { get; set; }
|
||||
public string? Type { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>/// DTO per la visualizzazione di un profilo nella lista
|
||||
/// </summary>
|
||||
public class DataCouplerProfileSummaryDto
|
||||
{
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace CredentialManager.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Tipo di mapping field
|
||||
/// </summary>
|
||||
public enum MappingType
|
||||
{
|
||||
/// <summary>
|
||||
/// Mapping da campo sorgente a campo destinazione
|
||||
/// </summary>
|
||||
FieldMapping,
|
||||
|
||||
/// <summary>
|
||||
/// Valore di default per campo destinazione
|
||||
/// </summary>
|
||||
DefaultValue
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rappresenta una voce di mapping che può essere:
|
||||
/// - Un mapping da campo sorgente a campo destinazione
|
||||
/// - Un valore di default per un campo destinazione
|
||||
/// </summary>
|
||||
public class FieldMappingEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Tipo di mapping
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
public MappingType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nome del campo sorgente (solo per FieldMapping)
|
||||
/// </summary>
|
||||
[JsonPropertyName("sourceField")]
|
||||
public string? SourceField { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nome del campo destinazione
|
||||
/// </summary>
|
||||
[JsonPropertyName("destinationField")]
|
||||
public string DestinationField { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Valore di default (solo per DefaultValue)
|
||||
/// </summary>
|
||||
[JsonPropertyName("defaultValue")]
|
||||
public object? DefaultValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tipo di dato del valore di default (per conversioni corrette)
|
||||
/// Esempi: "string", "int", "decimal", "boolean", "datetime"
|
||||
/// </summary>
|
||||
[JsonPropertyName("defaultValueType")]
|
||||
public string? DefaultValueType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Crea un mapping da campo sorgente a campo destinazione
|
||||
/// </summary>
|
||||
public static FieldMappingEntry CreateFieldMapping(string sourceField, string destinationField)
|
||||
{
|
||||
return new FieldMappingEntry
|
||||
{
|
||||
Type = MappingType.FieldMapping,
|
||||
SourceField = sourceField,
|
||||
DestinationField = destinationField
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crea un valore di default per un campo destinazione
|
||||
/// </summary>
|
||||
public static FieldMappingEntry CreateDefaultValue(string destinationField, object defaultValue, string? valueType = null)
|
||||
{
|
||||
return new FieldMappingEntry
|
||||
{
|
||||
Type = MappingType.DefaultValue,
|
||||
DestinationField = destinationField,
|
||||
DefaultValue = defaultValue,
|
||||
DefaultValueType = valueType ?? InferValueType(defaultValue)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determina automaticamente il tipo del valore
|
||||
/// </summary>
|
||||
private static string InferValueType(object? value)
|
||||
{
|
||||
if (value == null) return "string";
|
||||
|
||||
return value switch
|
||||
{
|
||||
string _ => "string",
|
||||
int _ => "int",
|
||||
long _ => "long",
|
||||
decimal _ => "decimal",
|
||||
double _ => "double",
|
||||
float _ => "float",
|
||||
bool _ => "boolean",
|
||||
DateTime _ => "datetime",
|
||||
DateTimeOffset _ => "datetimeoffset",
|
||||
_ => "string"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene una descrizione user-friendly del mapping
|
||||
/// </summary>
|
||||
public string GetDescription()
|
||||
{
|
||||
return Type switch
|
||||
{
|
||||
MappingType.FieldMapping => $"{SourceField} → {DestinationField}",
|
||||
MappingType.DefaultValue => $"{DestinationField} = {DefaultValue ?? "null"} ({DefaultValueType})",
|
||||
_ => "Unknown"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper per la conversione tra vecchio formato (Dictionary) e nuovo formato (FieldMappingEntry)
|
||||
/// </summary>
|
||||
public static class MappingConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converte il vecchio formato Dictionary in lista di FieldMappingEntry
|
||||
/// </summary>
|
||||
public static List<FieldMappingEntry> FromDictionary(Dictionary<string, string> oldMappings)
|
||||
{
|
||||
var entries = new List<FieldMappingEntry>();
|
||||
|
||||
foreach (var mapping in oldMappings)
|
||||
{
|
||||
entries.Add(FieldMappingEntry.CreateFieldMapping(mapping.Key, mapping.Value));
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converte una lista di FieldMappingEntry nel vecchio formato Dictionary (solo field mappings)
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> ToDictionary(List<FieldMappingEntry> entries)
|
||||
{
|
||||
var dictionary = new Dictionary<string, string>();
|
||||
|
||||
foreach (var entry in entries.Where(e => e.Type == MappingType.FieldMapping && !string.IsNullOrEmpty(e.SourceField)))
|
||||
{
|
||||
dictionary[entry.SourceField!] = entry.DestinationField;
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene solo i valori di default da una lista di entries
|
||||
/// </summary>
|
||||
public static Dictionary<string, (object? Value, string? Type)> GetDefaultValues(List<FieldMappingEntry> entries)
|
||||
{
|
||||
var defaults = new Dictionary<string, (object?, string?)>();
|
||||
|
||||
foreach (var entry in entries.Where(e => e.Type == MappingType.DefaultValue))
|
||||
{
|
||||
defaults[entry.DestinationField] = (entry.DefaultValue, entry.DefaultValueType);
|
||||
}
|
||||
|
||||
return defaults;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +216,7 @@ public class DataCouplerProfileService : IDataCouplerProfileService
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Deserializza il JSON delle External ID Relationships
|
||||
/// </summary>
|
||||
@@ -236,6 +237,64 @@ public class DataCouplerProfileService : IDataCouplerProfileService
|
||||
return new List<ExternalIdRelationshipDto>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializza i default values in JSON
|
||||
/// </summary>
|
||||
public string SerializeDefaultValues(Dictionary<string, (object? Value, string? Type)>? defaultValues)
|
||||
{
|
||||
if (defaultValues == null || !defaultValues.Any())
|
||||
return string.Empty;
|
||||
|
||||
// Converti in un formato serializzabile (Dictionary<string, DefaultValueDto>)
|
||||
var serializable = new Dictionary<string, DefaultValueDto>();
|
||||
foreach (var entry in defaultValues)
|
||||
{
|
||||
serializable[entry.Key] = new DefaultValueDto
|
||||
{
|
||||
Value = entry.Value.Value,
|
||||
Type = entry.Value.Type
|
||||
};
|
||||
}
|
||||
|
||||
return JsonSerializer.Serialize(serializable, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializza il JSON dei default values
|
||||
/// </summary>
|
||||
public Dictionary<string, (object? Value, string? Type)> DeserializeDefaultValues(string? json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
return new Dictionary<string, (object?, string?)>();
|
||||
|
||||
try
|
||||
{
|
||||
var deserialized = JsonSerializer.Deserialize<Dictionary<string, DefaultValueDto>>(json, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
});
|
||||
|
||||
if (deserialized == null)
|
||||
return new Dictionary<string, (object?, string?)>();
|
||||
|
||||
// Converti nel formato tuple
|
||||
var result = new Dictionary<string, (object?, string?)>();
|
||||
foreach (var entry in deserialized)
|
||||
{
|
||||
result[entry.Key] = (entry.Value.Value, entry.Value.Type);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new Dictionary<string, (object?, string?)>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converte un DataCouplerProfile in DTO
|
||||
@@ -262,6 +321,7 @@ public class DataCouplerProfileService : IDataCouplerProfileService
|
||||
DestinationTable = profile.DestinationTable,
|
||||
DestinationEndpoint = profile.DestinationEndpoint,
|
||||
FieldMappings = DeserializeFieldMappings(profile.FieldMappingJson),
|
||||
DefaultValues = DeserializeDefaultValues(profile.DefaultValuesJson),
|
||||
ExternalIdRelationships = DeserializeExternalIdRelationships(profile.ExternalIdRelationshipsJson),
|
||||
SourceKeyField = profile.SourceKeyField,
|
||||
UseRecordAssociations = profile.UseRecordAssociations
|
||||
@@ -291,6 +351,7 @@ public class DataCouplerProfileService : IDataCouplerProfileService
|
||||
DestinationTable = dto.DestinationTable,
|
||||
DestinationEndpoint = dto.DestinationEndpoint,
|
||||
FieldMappingJson = SerializeFieldMappings(dto.FieldMappings),
|
||||
DefaultValuesJson = SerializeDefaultValues(dto.DefaultValues),
|
||||
ExternalIdRelationshipsJson = SerializeExternalIdRelationships(dto.ExternalIdRelationships),
|
||||
SourceKeyField = dto.SourceKeyField,
|
||||
UseRecordAssociations = dto.UseRecordAssociations,
|
||||
|
||||
Reference in New Issue
Block a user