[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:
Alessio Dal Santo
2026-02-16 14:42:03 +01:00
parent 483eb7b407
commit b9670ae426
10 changed files with 1249 additions and 65 deletions
@@ -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
{
+174
View File
@@ -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,