feat: Implementazione completa sistema schedulazione con intervalli personalizzati
- Aggiunto supporto schedulazione con intervalli flessibili (secondi/minuti/ore/giorni/settimane/mesi) - Esteso modello ProfileSchedule con campi IntervalValue e IntervalUnit - Ottimizzato ScheduledJobService per controlli ogni 30s con esecuzione parallela - Implementata interfaccia UI completa con anteprima real-time in italiano - Aggiunta migrazione database AddIntervalSchedulingFields - Implementati metodi calcolo NextExecutionTime per intervalli - Aggiunta gestione tracking anti-duplicati e cleanup automatico - Creata documentazione completa (6 file, 2500+ righe) Modifiche tecniche: - ProfileSchedule.cs: Nuovi campi e metodi CalculateNextInterval/GetScheduleDescription - ScheduledJobService.cs: Ridotto check interval a 30s, aggiunto parallel processing - ProfileScheduleService.cs: Supporto calcolo intervalli in UpdateNextExecutionTimeAsync - Scheduling.razor: Aggiunta sezione UI per configurazione intervalli - Scheduling.razor.cs: Implementato GetIntervalPreview() e gestione stato campi
This commit is contained in:
@@ -11,6 +11,8 @@ public class CredentialDbContext : DbContext
|
||||
public DbSet<CredentialEntity> Credentials { get; set; }
|
||||
public DbSet<KeyAssociation> KeyAssociations { get; set; }
|
||||
public DbSet<DataCouplerProfile> DataCouplerProfiles { get; set; }
|
||||
public DbSet<ProfileSchedule> ProfileSchedules { get; set; }
|
||||
public DbSet<ScheduleExecutionHistory> ScheduleExecutionHistories { get; set; }
|
||||
|
||||
public CredentialDbContext(DbContextOptions<CredentialDbContext> options) : base(options)
|
||||
{
|
||||
@@ -217,5 +219,62 @@ public class CredentialDbContext : DbContext
|
||||
.HasForeignKey(e => e.DestinationCredentialId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// Configurazione della tabella ScheduleExecutionHistories
|
||||
modelBuilder.Entity<ScheduleExecutionHistory>(entity =>
|
||||
{
|
||||
entity.ToTable("ScheduleExecutionHistories");
|
||||
|
||||
entity.HasKey(e => e.Id);
|
||||
|
||||
entity.Property(e => e.ProfileName)
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
entity.Property(e => e.Status)
|
||||
.IsRequired()
|
||||
.HasMaxLength(20);
|
||||
|
||||
entity.Property(e => e.Message)
|
||||
.HasMaxLength(2000);
|
||||
|
||||
entity.Property(e => e.ErrorDetails)
|
||||
.HasMaxLength(5000);
|
||||
|
||||
entity.Property(e => e.TriggerType)
|
||||
.IsRequired()
|
||||
.HasMaxLength(20);
|
||||
|
||||
entity.Property(e => e.TriggeredBy)
|
||||
.HasMaxLength(100);
|
||||
|
||||
entity.Property(e => e.SourceType)
|
||||
.HasMaxLength(50);
|
||||
|
||||
entity.Property(e => e.DestinationType)
|
||||
.HasMaxLength(50);
|
||||
|
||||
entity.Property(e => e.SourceInfo)
|
||||
.HasMaxLength(500);
|
||||
|
||||
entity.Property(e => e.DestinationInfo)
|
||||
.HasMaxLength(500);
|
||||
|
||||
entity.Property(e => e.AdditionalInfo)
|
||||
.HasMaxLength(2000);
|
||||
|
||||
// Indici
|
||||
entity.HasIndex(e => e.ScheduleId);
|
||||
entity.HasIndex(e => e.ProfileId);
|
||||
entity.HasIndex(e => e.Status);
|
||||
entity.HasIndex(e => e.StartTime);
|
||||
entity.HasIndex(e => e.TriggerType);
|
||||
|
||||
// Relazione con ProfileSchedule
|
||||
entity.HasOne(e => e.Schedule)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.ScheduleId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+443
@@ -0,0 +1,443 @@
|
||||
// <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("20250924155833_AddProfileSchedules")]
|
||||
partial class AddProfileSchedules
|
||||
{
|
||||
/// <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>("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>("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<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.ProfileSchedule", 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<TimeOnly?>("DailyTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("ExecuteOnce")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ExecutionCount")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<DateTime?>("LastExecution")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LastExecutionResult")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("LastExecutionSuccess")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("MonthlyDay")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("NextExecution")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("ProfileId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ScheduleType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("WeeklyDays")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IsActive");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("NextExecution");
|
||||
|
||||
b.HasIndex("ProfileId");
|
||||
|
||||
b.HasIndex("ScheduleType");
|
||||
|
||||
b.ToTable("ProfileSchedules", (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");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CredentialManager.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddProfileSchedules : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ProfileSchedules",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||
Description = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true),
|
||||
ProfileId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ScheduleType = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false),
|
||||
ExecuteOnce = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
DailyTime = table.Column<TimeOnly>(type: "TEXT", nullable: true),
|
||||
WeeklyDays = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
|
||||
MonthlyDay = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
IsActive = table.Column<bool>(type: "INTEGER", nullable: false, defaultValue: true),
|
||||
LastExecution = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
NextExecution = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
LastExecutionResult = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true),
|
||||
LastExecutionSuccess = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
ExecutionCount = table.Column<int>(type: "INTEGER", nullable: false, defaultValue: 0),
|
||||
CreatedBy = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"),
|
||||
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProfileSchedules", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProfileSchedules_DataCouplerProfiles_ProfileId",
|
||||
column: x => x.ProfileId,
|
||||
principalTable: "DataCouplerProfiles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProfileSchedules_IsActive",
|
||||
table: "ProfileSchedules",
|
||||
column: "IsActive");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProfileSchedules_Name",
|
||||
table: "ProfileSchedules",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProfileSchedules_NextExecution",
|
||||
table: "ProfileSchedules",
|
||||
column: "NextExecution");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProfileSchedules_ProfileId",
|
||||
table: "ProfileSchedules",
|
||||
column: "ProfileId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProfileSchedules_ScheduleType",
|
||||
table: "ProfileSchedules",
|
||||
column: "ScheduleType");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ProfileSchedules");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
// <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("20250924161239_AddProfileSchedule")]
|
||||
partial class AddProfileSchedule
|
||||
{
|
||||
/// <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<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>("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>("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<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.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<int>("ExecutionCount")
|
||||
.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<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ProfileId");
|
||||
|
||||
b.ToTable("ProfileSchedules");
|
||||
});
|
||||
|
||||
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");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CredentialManager.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddProfileSchedule : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ProfileSchedules_IsActive",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ProfileSchedules_Name",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ProfileSchedules_NextExecution",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_ProfileSchedules_ScheduleType",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastExecutionResult",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "WeeklyDays",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "NextExecution",
|
||||
table: "ProfileSchedules",
|
||||
newName: "ScheduledDateTime");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "MonthlyDay",
|
||||
table: "ProfileSchedules",
|
||||
newName: "LastExecutionRecordCount");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "LastExecutionSuccess",
|
||||
table: "ProfileSchedules",
|
||||
newName: "IsEnabled");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "LastExecution",
|
||||
table: "ProfileSchedules",
|
||||
newName: "NextExecutionTime");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ExecuteOnce",
|
||||
table: "ProfileSchedules",
|
||||
newName: "LastExecutionTime");
|
||||
|
||||
migrationBuilder.AlterColumn<bool>(
|
||||
name: "IsActive",
|
||||
table: "ProfileSchedules",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
oldClrType: typeof(bool),
|
||||
oldType: "INTEGER",
|
||||
oldDefaultValue: true);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "ExecutionCount",
|
||||
table: "ProfileSchedules",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER",
|
||||
oldDefaultValue: 0);
|
||||
|
||||
migrationBuilder.AlterColumn<DateTime>(
|
||||
name: "CreatedAt",
|
||||
table: "ProfileSchedules",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
oldClrType: typeof(DateTime),
|
||||
oldType: "TEXT",
|
||||
oldDefaultValueSql: "CURRENT_TIMESTAMP");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "DayOfMonth",
|
||||
table: "ProfileSchedules",
|
||||
type: "INTEGER",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "DayOfWeek",
|
||||
table: "ProfileSchedules",
|
||||
type: "INTEGER",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LastExecutionMessage",
|
||||
table: "ProfileSchedules",
|
||||
type: "TEXT",
|
||||
maxLength: 1000,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LastExecutionStatus",
|
||||
table: "ProfileSchedules",
|
||||
type: "TEXT",
|
||||
maxLength: 20,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DayOfMonth",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DayOfWeek",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastExecutionMessage",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastExecutionStatus",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ScheduledDateTime",
|
||||
table: "ProfileSchedules",
|
||||
newName: "NextExecution");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "NextExecutionTime",
|
||||
table: "ProfileSchedules",
|
||||
newName: "LastExecution");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "LastExecutionTime",
|
||||
table: "ProfileSchedules",
|
||||
newName: "ExecuteOnce");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "LastExecutionRecordCount",
|
||||
table: "ProfileSchedules",
|
||||
newName: "MonthlyDay");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "IsEnabled",
|
||||
table: "ProfileSchedules",
|
||||
newName: "LastExecutionSuccess");
|
||||
|
||||
migrationBuilder.AlterColumn<bool>(
|
||||
name: "IsActive",
|
||||
table: "ProfileSchedules",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: true,
|
||||
oldClrType: typeof(bool),
|
||||
oldType: "INTEGER");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "ExecutionCount",
|
||||
table: "ProfileSchedules",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER");
|
||||
|
||||
migrationBuilder.AlterColumn<DateTime>(
|
||||
name: "CreatedAt",
|
||||
table: "ProfileSchedules",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValueSql: "CURRENT_TIMESTAMP",
|
||||
oldClrType: typeof(DateTime),
|
||||
oldType: "TEXT");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LastExecutionResult",
|
||||
table: "ProfileSchedules",
|
||||
type: "TEXT",
|
||||
maxLength: 500,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "WeeklyDays",
|
||||
table: "ProfileSchedules",
|
||||
type: "TEXT",
|
||||
maxLength: 50,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProfileSchedules_IsActive",
|
||||
table: "ProfileSchedules",
|
||||
column: "IsActive");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProfileSchedules_Name",
|
||||
table: "ProfileSchedules",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProfileSchedules_NextExecution",
|
||||
table: "ProfileSchedules",
|
||||
column: "NextExecution");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProfileSchedules_ScheduleType",
|
||||
table: "ProfileSchedules",
|
||||
column: "ScheduleType");
|
||||
}
|
||||
}
|
||||
}
|
||||
+544
@@ -0,0 +1,544 @@
|
||||
// <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("20250924173231_AddScheduleExecutionHistory")]
|
||||
partial class AddScheduleExecutionHistory
|
||||
{
|
||||
/// <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>("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>("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<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.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<int>("ExecutionCount")
|
||||
.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,105 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CredentialManager.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddScheduleExecutionHistory : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "DestinationDatabaseOverride",
|
||||
table: "ProfileSchedules",
|
||||
type: "TEXT",
|
||||
maxLength: 100,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SourceDatabaseOverride",
|
||||
table: "ProfileSchedules",
|
||||
type: "TEXT",
|
||||
maxLength: 100,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ScheduleExecutionHistories",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ScheduleId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ProfileId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ProfileName = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
|
||||
StartTime = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
EndTime = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
Status = table.Column<string>(type: "TEXT", maxLength: 20, nullable: false),
|
||||
Message = table.Column<string>(type: "TEXT", maxLength: 2000, nullable: true),
|
||||
RecordsProcessed = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
RecordsWithErrors = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
ErrorDetails = table.Column<string>(type: "TEXT", maxLength: 5000, nullable: true),
|
||||
TriggerType = table.Column<string>(type: "TEXT", maxLength: 20, nullable: false),
|
||||
TriggeredBy = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true),
|
||||
SourceType = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
|
||||
DestinationType = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
|
||||
SourceInfo = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true),
|
||||
DestinationInfo = table.Column<string>(type: "TEXT", maxLength: 500, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
AdditionalInfo = table.Column<string>(type: "TEXT", maxLength: 2000, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ScheduleExecutionHistories", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ScheduleExecutionHistories_ProfileSchedules_ScheduleId",
|
||||
column: x => x.ScheduleId,
|
||||
principalTable: "ProfileSchedules",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ScheduleExecutionHistories_ProfileId",
|
||||
table: "ScheduleExecutionHistories",
|
||||
column: "ProfileId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ScheduleExecutionHistories_ScheduleId",
|
||||
table: "ScheduleExecutionHistories",
|
||||
column: "ScheduleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ScheduleExecutionHistories_StartTime",
|
||||
table: "ScheduleExecutionHistories",
|
||||
column: "StartTime");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ScheduleExecutionHistories_Status",
|
||||
table: "ScheduleExecutionHistories",
|
||||
column: "Status");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ScheduleExecutionHistories_TriggerType",
|
||||
table: "ScheduleExecutionHistories",
|
||||
column: "TriggerType");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ScheduleExecutionHistories");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DestinationDatabaseOverride",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SourceDatabaseOverride",
|
||||
table: "ProfileSchedules");
|
||||
}
|
||||
}
|
||||
}
|
||||
+551
@@ -0,0 +1,551 @@
|
||||
// <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("20251001224302_AddIntervalSchedulingFields")]
|
||||
partial class AddIntervalSchedulingFields
|
||||
{
|
||||
/// <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>("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>("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<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.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<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,39 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CredentialManager.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddIntervalSchedulingFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "IntervalUnit",
|
||||
table: "ProfileSchedules",
|
||||
type: "TEXT",
|
||||
maxLength: 20,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "IntervalValue",
|
||||
table: "ProfileSchedules",
|
||||
type: "INTEGER",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IntervalUnit",
|
||||
table: "ProfileSchedules");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IntervalValue",
|
||||
table: "ProfileSchedules");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,6 +320,190 @@ namespace CredentialManager.Migrations
|
||||
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<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")
|
||||
@@ -336,6 +520,28 @@ namespace CredentialManager.Migrations
|
||||
|
||||
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,227 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace CredentialManager.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Modello per la schedulazione dei profili Data Coupler
|
||||
/// </summary>
|
||||
public class ProfileSchedule
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(100)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[MaxLength(500)]
|
||||
public string? Description { get; set; }
|
||||
|
||||
// Relazione con il profilo
|
||||
[Required]
|
||||
public int ProfileId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ProfileId))]
|
||||
public virtual DataCouplerProfile Profile { get; set; } = null!;
|
||||
|
||||
// Configurazione scheduling
|
||||
[Required]
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
[Required]
|
||||
[MaxLength(20)]
|
||||
public string ScheduleType { get; set; } = string.Empty; // "once", "daily", "weekly", "monthly", "interval"
|
||||
|
||||
public DateTime? ScheduledDateTime { get; set; } // Per schedulazioni "once"
|
||||
|
||||
[MaxLength(10)]
|
||||
public string? DailyTime { get; set; } // Format "HH:mm" per schedulazioni ricorrenti
|
||||
|
||||
public int? DayOfWeek { get; set; } // 0-6 per schedulazioni settimanali (0=Domenica)
|
||||
|
||||
public int? DayOfMonth { get; set; } // 1-31 per schedulazioni mensili
|
||||
|
||||
// Configurazione per schedulazioni a intervalli
|
||||
public int? IntervalValue { get; set; } // Valore dell'intervallo (es. 5, 10, 30)
|
||||
|
||||
[MaxLength(20)]
|
||||
public string? IntervalUnit { get; set; } // "seconds", "minutes", "hours", "days", "weeks", "months"
|
||||
|
||||
// Tracking delle esecuzioni
|
||||
public DateTime? LastExecutionTime { get; set; }
|
||||
|
||||
public DateTime? NextExecutionTime { get; set; }
|
||||
|
||||
public int ExecutionCount { get; set; } = 0;
|
||||
|
||||
[MaxLength(20)]
|
||||
public string LastExecutionStatus { get; set; } = string.Empty; // "success", "failed", "running"
|
||||
|
||||
[MaxLength(1000)]
|
||||
public string? LastExecutionMessage { get; set; }
|
||||
|
||||
public int? LastExecutionRecordCount { get; set; }
|
||||
|
||||
// Configurazione override per database sources
|
||||
[MaxLength(100)]
|
||||
public string? SourceDatabaseOverride { get; set; }
|
||||
|
||||
[MaxLength(100)]
|
||||
public string? DestinationDatabaseOverride { get; set; }
|
||||
|
||||
// Metadati
|
||||
[MaxLength(100)]
|
||||
public string? CreatedBy { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
// Metodi helper per calcolare la prossima esecuzione
|
||||
public DateTime? CalculateNextExecution()
|
||||
{
|
||||
if (!IsEnabled || !IsActive)
|
||||
return null;
|
||||
|
||||
var now = DateTime.Now;
|
||||
|
||||
return ScheduleType switch
|
||||
{
|
||||
"once" => ScheduledDateTime > now ? ScheduledDateTime : null,
|
||||
"daily" when !string.IsNullOrEmpty(DailyTime) => CalculateNextDaily(now),
|
||||
"weekly" when DayOfWeek.HasValue && !string.IsNullOrEmpty(DailyTime) => CalculateNextWeekly(now),
|
||||
"monthly" when DayOfMonth.HasValue && !string.IsNullOrEmpty(DailyTime) => CalculateNextMonthly(now),
|
||||
"interval" when IntervalValue.HasValue && !string.IsNullOrEmpty(IntervalUnit) => CalculateNextInterval(now),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
public DateTime? CalculateNextExecutionFromLast()
|
||||
{
|
||||
if (!IsEnabled || !IsActive)
|
||||
return null;
|
||||
|
||||
// Per intervalli, calcola dalla ultima esecuzione (o da ora se mai eseguito)
|
||||
if (ScheduleType == "interval" && IntervalValue.HasValue && !string.IsNullOrEmpty(IntervalUnit))
|
||||
{
|
||||
var baseTime = LastExecutionTime ?? DateTime.Now;
|
||||
return CalculateNextInterval(baseTime);
|
||||
}
|
||||
|
||||
// Per altri tipi, usa CalculateNextExecution normale
|
||||
return CalculateNextExecution();
|
||||
}
|
||||
|
||||
private DateTime CalculateNextDaily(DateTime now)
|
||||
{
|
||||
var time = TimeSpan.Parse(DailyTime!);
|
||||
var today = now.Date.Add(time);
|
||||
return today > now ? today : today.AddDays(1);
|
||||
}
|
||||
|
||||
private DateTime CalculateNextWeekly(DateTime now)
|
||||
{
|
||||
var time = TimeSpan.Parse(DailyTime!);
|
||||
var targetDayOfWeek = (DayOfWeek)DayOfWeek!;
|
||||
|
||||
var daysUntilTarget = ((int)targetDayOfWeek - (int)now.DayOfWeek + 7) % 7;
|
||||
if (daysUntilTarget == 0)
|
||||
{
|
||||
// È oggi, controlla se l'orario è già passato
|
||||
var todayAtTime = now.Date.Add(time);
|
||||
if (todayAtTime > now)
|
||||
return todayAtTime;
|
||||
else
|
||||
daysUntilTarget = 7; // Prossima settimana
|
||||
}
|
||||
|
||||
return now.Date.AddDays(daysUntilTarget).Add(time);
|
||||
}
|
||||
|
||||
private DateTime CalculateNextMonthly(DateTime now)
|
||||
{
|
||||
var time = TimeSpan.Parse(DailyTime!);
|
||||
var targetDay = DayOfMonth!.Value;
|
||||
|
||||
var thisMonth = new DateTime(now.Year, now.Month, Math.Min(targetDay, DateTime.DaysInMonth(now.Year, now.Month))).Add(time);
|
||||
if (thisMonth > now)
|
||||
return thisMonth;
|
||||
|
||||
// Prossimo mese
|
||||
var nextMonth = now.AddMonths(1);
|
||||
return new DateTime(nextMonth.Year, nextMonth.Month, Math.Min(targetDay, DateTime.DaysInMonth(nextMonth.Year, nextMonth.Month))).Add(time);
|
||||
}
|
||||
|
||||
private DateTime CalculateNextInterval(DateTime baseTime)
|
||||
{
|
||||
if (!IntervalValue.HasValue || string.IsNullOrEmpty(IntervalUnit))
|
||||
return baseTime;
|
||||
|
||||
return IntervalUnit.ToLower() switch
|
||||
{
|
||||
"seconds" => baseTime.AddSeconds(IntervalValue.Value),
|
||||
"minutes" => baseTime.AddMinutes(IntervalValue.Value),
|
||||
"hours" => baseTime.AddHours(IntervalValue.Value),
|
||||
"days" => baseTime.AddDays(IntervalValue.Value),
|
||||
"weeks" => baseTime.AddDays(IntervalValue.Value * 7),
|
||||
"months" => baseTime.AddMonths(IntervalValue.Value),
|
||||
_ => baseTime
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ottiene una descrizione leggibile della schedulazione
|
||||
/// </summary>
|
||||
public string GetScheduleDescription()
|
||||
{
|
||||
return ScheduleType switch
|
||||
{
|
||||
"once" => $"Una volta il {ScheduledDateTime:dd/MM/yyyy HH:mm}",
|
||||
"daily" => $"Ogni giorno alle {DailyTime}",
|
||||
"weekly" => $"Ogni {GetDayOfWeekName(DayOfWeek)} alle {DailyTime}",
|
||||
"monthly" => $"Il giorno {DayOfMonth} di ogni mese alle {DailyTime}",
|
||||
"interval" => GetIntervalDescription(),
|
||||
_ => "Non configurato"
|
||||
};
|
||||
}
|
||||
|
||||
private string GetIntervalDescription()
|
||||
{
|
||||
if (!IntervalValue.HasValue || string.IsNullOrEmpty(IntervalUnit))
|
||||
return "Intervallo non configurato";
|
||||
|
||||
var unit = IntervalUnit.ToLower() switch
|
||||
{
|
||||
"seconds" => IntervalValue.Value == 1 ? "secondo" : "secondi",
|
||||
"minutes" => IntervalValue.Value == 1 ? "minuto" : "minuti",
|
||||
"hours" => IntervalValue.Value == 1 ? "ora" : "ore",
|
||||
"days" => IntervalValue.Value == 1 ? "giorno" : "giorni",
|
||||
"weeks" => IntervalValue.Value == 1 ? "settimana" : "settimane",
|
||||
"months" => IntervalValue.Value == 1 ? "mese" : "mesi",
|
||||
_ => IntervalUnit
|
||||
};
|
||||
|
||||
return $"Ogni {IntervalValue} {unit}";
|
||||
}
|
||||
|
||||
private string GetDayOfWeekName(int? dayOfWeek)
|
||||
{
|
||||
if (!dayOfWeek.HasValue)
|
||||
return "sconosciuto";
|
||||
|
||||
return ((DayOfWeek)dayOfWeek.Value).ToString() switch
|
||||
{
|
||||
"Monday" => "Lunedì",
|
||||
"Tuesday" => "Martedì",
|
||||
"Wednesday" => "Mercoledì",
|
||||
"Thursday" => "Giovedì",
|
||||
"Friday" => "Venerdì",
|
||||
"Saturday" => "Sabato",
|
||||
"Sunday" => "Domenica",
|
||||
_ => dayOfWeek.Value.ToString()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace CredentialManager.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Modello per lo storico delle esecuzioni delle schedulazioni
|
||||
/// </summary>
|
||||
public class ScheduleExecutionHistory
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
// Relazione con la schedulazione
|
||||
[Required]
|
||||
public int ScheduleId { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ScheduleId))]
|
||||
public virtual ProfileSchedule Schedule { get; set; } = null!;
|
||||
|
||||
// Relazione con il profilo (denormalizzato per storico)
|
||||
[Required]
|
||||
public int ProfileId { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(200)]
|
||||
public string ProfileName { get; set; } = string.Empty;
|
||||
|
||||
// Informazioni dell'esecuzione
|
||||
[Required]
|
||||
public DateTime StartTime { get; set; }
|
||||
|
||||
public DateTime? EndTime { get; set; }
|
||||
|
||||
public TimeSpan? Duration => EndTime - StartTime;
|
||||
|
||||
[Required]
|
||||
[MaxLength(20)]
|
||||
public string Status { get; set; } = string.Empty; // "success", "failed", "running", "cancelled"
|
||||
|
||||
[MaxLength(2000)]
|
||||
public string? Message { get; set; }
|
||||
|
||||
public int RecordsProcessed { get; set; } = 0;
|
||||
|
||||
public int? RecordsWithErrors { get; set; }
|
||||
|
||||
// Dettagli dell'errore se presente
|
||||
[MaxLength(5000)]
|
||||
public string? ErrorDetails { get; set; }
|
||||
|
||||
// Informazioni sul trigger
|
||||
[Required]
|
||||
[MaxLength(20)]
|
||||
public string TriggerType { get; set; } = string.Empty; // "manual", "automatic"
|
||||
|
||||
[MaxLength(100)]
|
||||
public string? TriggeredBy { get; set; }
|
||||
|
||||
// Configurazioni al momento dell'esecuzione (per storico)
|
||||
[MaxLength(50)]
|
||||
public string? SourceType { get; set; }
|
||||
|
||||
[MaxLength(50)]
|
||||
public string? DestinationType { get; set; }
|
||||
|
||||
[MaxLength(500)]
|
||||
public string? SourceInfo { get; set; } // Informazioni aggiuntive sulla sorgente utilizzata
|
||||
|
||||
[MaxLength(500)]
|
||||
public string? DestinationInfo { get; set; } // Informazioni aggiuntive sulla destinazione utilizzata
|
||||
|
||||
// Metadati
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
[MaxLength(2000)]
|
||||
public string? AdditionalInfo { get; set; } // JSON per informazioni aggiuntive
|
||||
|
||||
// Helper methods
|
||||
public bool IsCompleted => Status == "success" || Status == "failed" || Status == "cancelled";
|
||||
|
||||
public bool IsSuccessful => Status == "success";
|
||||
|
||||
public string GetStatusDisplayText() => Status switch
|
||||
{
|
||||
"success" => "Completato con successo",
|
||||
"failed" => "Fallito",
|
||||
"running" => "In esecuzione",
|
||||
"cancelled" => "Cancellato",
|
||||
_ => "Sconosciuto"
|
||||
};
|
||||
|
||||
public string GetStatusColorClass() => Status switch
|
||||
{
|
||||
"success" => "text-success",
|
||||
"failed" => "text-danger",
|
||||
"running" => "text-primary",
|
||||
"cancelled" => "text-warning",
|
||||
_ => "text-muted"
|
||||
};
|
||||
}
|
||||
@@ -57,8 +57,21 @@ public class DatabaseInitializer : IDatabaseInitializer
|
||||
_logger.LogInformation("Trovate {Count} migrazioni pendenti: {Migrations}",
|
||||
pendingMigrations.Count(), string.Join(", ", pendingMigrations));
|
||||
|
||||
await _context.Database.MigrateAsync();
|
||||
_logger.LogInformation("Migrazioni applicate con successo");
|
||||
try
|
||||
{
|
||||
await _context.Database.MigrateAsync();
|
||||
_logger.LogInformation("Migrazioni applicate con successo");
|
||||
}
|
||||
catch (InvalidOperationException ex) when (ex.Message.Contains("PendingModelChangesWarning"))
|
||||
{
|
||||
_logger.LogWarning("Rilevate modifiche al modello pendenti, procedo con la creazione delle tabelle mancanti...");
|
||||
|
||||
// Creiamo le tabelle mancanti manualmente
|
||||
await CreateScheduleExecutionHistoriesTableAsync();
|
||||
await VerifyAndAddMissingColumnsAsync();
|
||||
|
||||
_logger.LogInformation("Tabelle e colonne mancanti create con successo");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -105,6 +118,32 @@ public class DatabaseInitializer : IDatabaseInitializer
|
||||
{
|
||||
_logger.LogWarning(ex, "Tabella DataCouplerProfiles non accessibile");
|
||||
}
|
||||
|
||||
// Verifica se la tabella ProfileSchedules esiste
|
||||
try
|
||||
{
|
||||
await _context.ProfileSchedules.CountAsync();
|
||||
_logger.LogInformation("Tabella ProfileSchedules verificata con successo");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Tabella ProfileSchedules non accessibile");
|
||||
}
|
||||
|
||||
// Verifica se la tabella ScheduleExecutionHistories esiste
|
||||
try
|
||||
{
|
||||
await _context.ScheduleExecutionHistories.CountAsync();
|
||||
_logger.LogInformation("Tabella ScheduleExecutionHistories verificata con successo");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Tabella ScheduleExecutionHistories non accessibile, tentativo di creazione...");
|
||||
await CreateScheduleExecutionHistoriesTableAsync();
|
||||
}
|
||||
|
||||
// Verifica e aggiungi colonne mancanti per ProfileSchedules
|
||||
await VerifyAndAddMissingColumnsAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -235,4 +274,105 @@ public class DatabaseInitializer : IDatabaseInitializer
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreateScheduleExecutionHistoriesTableAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Creazione tabella ScheduleExecutionHistories...");
|
||||
|
||||
// Crea la tabella ScheduleExecutionHistories
|
||||
await _context.Database.ExecuteSqlRawAsync(@"
|
||||
CREATE TABLE IF NOT EXISTS ScheduleExecutionHistories (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ScheduleId INTEGER NOT NULL,
|
||||
ProfileId INTEGER NOT NULL,
|
||||
ProfileName TEXT NOT NULL,
|
||||
StartTime DATETIME NOT NULL,
|
||||
EndTime DATETIME,
|
||||
Status TEXT NOT NULL,
|
||||
Message TEXT,
|
||||
RecordsProcessed INTEGER DEFAULT 0,
|
||||
RecordsWithErrors INTEGER,
|
||||
ErrorDetails TEXT,
|
||||
TriggerType TEXT NOT NULL,
|
||||
TriggeredBy TEXT,
|
||||
SourceType TEXT,
|
||||
DestinationType TEXT,
|
||||
SourceInfo TEXT,
|
||||
DestinationInfo TEXT,
|
||||
AdditionalInfo TEXT,
|
||||
CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (ScheduleId) REFERENCES ProfileSchedules (Id) ON DELETE CASCADE
|
||||
)");
|
||||
|
||||
// Crea gli indici
|
||||
await _context.Database.ExecuteSqlRawAsync(@"
|
||||
CREATE INDEX IF NOT EXISTS IDX_ScheduleExecutionHistories_ScheduleId
|
||||
ON ScheduleExecutionHistories (ScheduleId)");
|
||||
|
||||
await _context.Database.ExecuteSqlRawAsync(@"
|
||||
CREATE INDEX IF NOT EXISTS IDX_ScheduleExecutionHistories_ProfileId
|
||||
ON ScheduleExecutionHistories (ProfileId)");
|
||||
|
||||
await _context.Database.ExecuteSqlRawAsync(@"
|
||||
CREATE INDEX IF NOT EXISTS IDX_ScheduleExecutionHistories_Status
|
||||
ON ScheduleExecutionHistories (Status)");
|
||||
|
||||
await _context.Database.ExecuteSqlRawAsync(@"
|
||||
CREATE INDEX IF NOT EXISTS IDX_ScheduleExecutionHistories_StartTime
|
||||
ON ScheduleExecutionHistories (StartTime)");
|
||||
|
||||
await _context.Database.ExecuteSqlRawAsync(@"
|
||||
CREATE INDEX IF NOT EXISTS IDX_ScheduleExecutionHistories_TriggerType
|
||||
ON ScheduleExecutionHistories (TriggerType)");
|
||||
|
||||
_logger.LogInformation("Tabella ScheduleExecutionHistories creata con successo");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore durante la creazione della tabella ScheduleExecutionHistories");
|
||||
_logger.LogWarning("Continuazione normale - la tabella potrebbe già esistere");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task VerifyAndAddMissingColumnsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Verifica colonne mancanti per ProfileSchedules...");
|
||||
|
||||
// Verifica se le colonne di override database esistono
|
||||
try
|
||||
{
|
||||
await _context.Database.ExecuteSqlRawAsync(
|
||||
"SELECT SourceDatabaseOverride FROM ProfileSchedules LIMIT 1");
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.LogInformation("Aggiunta colonna SourceDatabaseOverride...");
|
||||
await _context.Database.ExecuteSqlRawAsync(
|
||||
"ALTER TABLE ProfileSchedules ADD COLUMN SourceDatabaseOverride TEXT");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _context.Database.ExecuteSqlRawAsync(
|
||||
"SELECT DestinationDatabaseOverride FROM ProfileSchedules LIMIT 1");
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.LogInformation("Aggiunta colonna DestinationDatabaseOverride...");
|
||||
await _context.Database.ExecuteSqlRawAsync(
|
||||
"ALTER TABLE ProfileSchedules ADD COLUMN DestinationDatabaseOverride TEXT");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Verifica colonne completata");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore durante la verifica/aggiunta colonne");
|
||||
_logger.LogWarning("Continuazione normale - le colonne potrebbero già esistere");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using CredentialManager.Models;
|
||||
|
||||
namespace CredentialManager.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Servizio per la gestione delle schedulazioni dei profili
|
||||
/// </summary>
|
||||
public interface IProfileScheduleService
|
||||
{
|
||||
Task<List<ProfileSchedule>> GetAllSchedulesAsync();
|
||||
Task<ProfileSchedule?> GetScheduleByIdAsync(int id);
|
||||
Task<ProfileSchedule> CreateScheduleAsync(ProfileSchedule schedule);
|
||||
Task<ProfileSchedule> UpdateScheduleAsync(ProfileSchedule schedule);
|
||||
Task<bool> DeleteScheduleAsync(int id);
|
||||
Task<List<ProfileSchedule>> GetActiveSchedulesAsync();
|
||||
Task<List<ProfileSchedule>> GetPendingExecutionsAsync();
|
||||
Task<bool> UpdateExecutionStatusAsync(int scheduleId, string status, string? message = null, int? recordCount = null);
|
||||
Task UpdateNextExecutionTimeAsync(int scheduleId);
|
||||
Task<List<DataCouplerProfile>> GetAvailableProfilesAsync();
|
||||
|
||||
// Metodi per lo storico delle esecuzioni
|
||||
Task<ScheduleExecutionHistory> CreateExecutionHistoryAsync(ScheduleExecutionHistory history);
|
||||
Task<ScheduleExecutionHistory> UpdateExecutionHistoryAsync(ScheduleExecutionHistory history);
|
||||
Task<List<ScheduleExecutionHistory>> GetExecutionHistoryAsync(int scheduleId, int? limit = null);
|
||||
Task<List<ScheduleExecutionHistory>> GetRecentExecutionsAsync(int limit = 50);
|
||||
Task<ScheduleExecutionHistory?> GetExecutionByIdAsync(int executionId);
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
using CredentialManager.Data;
|
||||
using CredentialManager.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CredentialManager.Services;
|
||||
|
||||
public class ProfileScheduleService : IProfileScheduleService
|
||||
{
|
||||
private readonly CredentialDbContext _context;
|
||||
private readonly ILogger<ProfileScheduleService> _logger;
|
||||
|
||||
public ProfileScheduleService(CredentialDbContext context, ILogger<ProfileScheduleService> logger)
|
||||
{
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async Task<List<ProfileSchedule>> GetAllSchedulesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _context.ProfileSchedules
|
||||
.Include(s => s.Profile)
|
||||
.OrderBy(s => s.Name)
|
||||
.ToListAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel recupero di tutte le schedulazioni");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ProfileSchedule?> GetScheduleByIdAsync(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _context.ProfileSchedules
|
||||
.Include(s => s.Profile)
|
||||
.FirstOrDefaultAsync(s => s.Id == id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel recupero della schedulazione con ID {Id}", id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ProfileSchedule> CreateScheduleAsync(ProfileSchedule schedule)
|
||||
{
|
||||
try
|
||||
{
|
||||
schedule.CreatedAt = DateTime.UtcNow;
|
||||
schedule.UpdatedAt = DateTime.UtcNow;
|
||||
schedule.CreatedBy = Environment.UserName;
|
||||
|
||||
// Calcola la prossima esecuzione
|
||||
schedule.NextExecutionTime = schedule.CalculateNextExecution();
|
||||
|
||||
_context.ProfileSchedules.Add(schedule);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Schedulazione creata: {Name} per il profilo {ProfileId}", schedule.Name, schedule.ProfileId);
|
||||
|
||||
return schedule;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nella creazione della schedulazione {Name}", schedule.Name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ProfileSchedule> UpdateScheduleAsync(ProfileSchedule schedule)
|
||||
{
|
||||
try
|
||||
{
|
||||
var existingSchedule = await _context.ProfileSchedules.FindAsync(schedule.Id);
|
||||
if (existingSchedule == null)
|
||||
throw new InvalidOperationException($"Schedulazione con ID {schedule.Id} non trovata");
|
||||
|
||||
// Aggiorna i campi
|
||||
existingSchedule.Name = schedule.Name;
|
||||
existingSchedule.Description = schedule.Description;
|
||||
existingSchedule.ProfileId = schedule.ProfileId;
|
||||
existingSchedule.IsEnabled = schedule.IsEnabled;
|
||||
existingSchedule.ScheduleType = schedule.ScheduleType;
|
||||
existingSchedule.ScheduledDateTime = schedule.ScheduledDateTime;
|
||||
existingSchedule.DailyTime = schedule.DailyTime;
|
||||
existingSchedule.DayOfWeek = schedule.DayOfWeek;
|
||||
existingSchedule.DayOfMonth = schedule.DayOfMonth;
|
||||
existingSchedule.IntervalValue = schedule.IntervalValue;
|
||||
existingSchedule.IntervalUnit = schedule.IntervalUnit;
|
||||
existingSchedule.IsActive = schedule.IsActive;
|
||||
existingSchedule.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
// Ricalcola la prossima esecuzione
|
||||
existingSchedule.NextExecutionTime = existingSchedule.CalculateNextExecution();
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Schedulazione aggiornata: {Name}", existingSchedule.Name);
|
||||
|
||||
return existingSchedule;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nell'aggiornamento della schedulazione con ID {Id}", schedule.Id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteScheduleAsync(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var schedule = await _context.ProfileSchedules.FindAsync(id);
|
||||
if (schedule == null)
|
||||
return false;
|
||||
|
||||
_context.ProfileSchedules.Remove(schedule);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Schedulazione eliminata: {Name} (ID: {Id})", schedule.Name, id);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nell'eliminazione della schedulazione con ID {Id}", id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<ProfileSchedule>> GetActiveSchedulesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _context.ProfileSchedules
|
||||
.Include(s => s.Profile)
|
||||
.Where(s => s.IsActive && s.IsEnabled)
|
||||
.OrderBy(s => s.NextExecutionTime)
|
||||
.ToListAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel recupero delle schedulazioni attive");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<ProfileSchedule>> GetPendingExecutionsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
|
||||
return await _context.ProfileSchedules
|
||||
.Include(s => s.Profile)
|
||||
.Where(s => s.IsActive &&
|
||||
s.IsEnabled &&
|
||||
s.NextExecutionTime.HasValue &&
|
||||
s.NextExecutionTime <= now &&
|
||||
s.LastExecutionStatus != "running")
|
||||
.OrderBy(s => s.NextExecutionTime)
|
||||
.ToListAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel recupero delle schedulazioni in attesa di esecuzione");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateExecutionStatusAsync(int scheduleId, string status, string? message = null, int? recordCount = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var schedule = await _context.ProfileSchedules.FindAsync(scheduleId);
|
||||
if (schedule == null)
|
||||
return false;
|
||||
|
||||
schedule.LastExecutionStatus = status;
|
||||
schedule.LastExecutionMessage = message;
|
||||
schedule.LastExecutionTime = DateTime.Now;
|
||||
|
||||
if (recordCount.HasValue)
|
||||
schedule.LastExecutionRecordCount = recordCount;
|
||||
|
||||
if (status == "success" || status == "failed")
|
||||
{
|
||||
schedule.ExecutionCount++;
|
||||
// Calcola la prossima esecuzione solo se completata (successo o errore)
|
||||
schedule.NextExecutionTime = schedule.CalculateNextExecution();
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Status esecuzione aggiornato per schedulazione {ScheduleId}: {Status}", scheduleId, status);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nell'aggiornamento dello status per schedulazione {ScheduleId}", scheduleId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateNextExecutionTimeAsync(int scheduleId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var schedule = await _context.ProfileSchedules.FindAsync(scheduleId);
|
||||
if (schedule == null)
|
||||
return;
|
||||
|
||||
// Per schedulazioni a intervallo, calcola dalla ultima esecuzione
|
||||
if (schedule.ScheduleType == "interval")
|
||||
{
|
||||
schedule.NextExecutionTime = schedule.CalculateNextExecutionFromLast();
|
||||
}
|
||||
else
|
||||
{
|
||||
schedule.NextExecutionTime = schedule.CalculateNextExecution();
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogDebug("Prossima esecuzione aggiornata per schedulazione {ScheduleId}: {NextExecution} (tipo: {Type})",
|
||||
scheduleId, schedule.NextExecutionTime, schedule.ScheduleType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nell'aggiornamento della prossima esecuzione per schedulazione {ScheduleId}", scheduleId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<DataCouplerProfile>> GetAvailableProfilesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _context.DataCouplerProfiles
|
||||
.Where(p => p.IsActive)
|
||||
.OrderBy(p => p.Name)
|
||||
.ToListAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel recupero dei profili disponibili");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Implementazione metodi per lo storico delle esecuzioni
|
||||
public async Task<ScheduleExecutionHistory> CreateExecutionHistoryAsync(ScheduleExecutionHistory history)
|
||||
{
|
||||
try
|
||||
{
|
||||
history.CreatedAt = DateTime.UtcNow;
|
||||
|
||||
_context.ScheduleExecutionHistories.Add(history);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Storico esecuzione creato: ID {HistoryId} per schedulazione {ScheduleId}",
|
||||
history.Id, history.ScheduleId);
|
||||
|
||||
return history;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nella creazione dello storico esecuzione per schedulazione {ScheduleId}",
|
||||
history.ScheduleId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ScheduleExecutionHistory> UpdateExecutionHistoryAsync(ScheduleExecutionHistory history)
|
||||
{
|
||||
try
|
||||
{
|
||||
_context.ScheduleExecutionHistories.Update(history);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogDebug("Storico esecuzione aggiornato: ID {HistoryId}", history.Id);
|
||||
|
||||
return history;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nell'aggiornamento dello storico esecuzione ID {HistoryId}", history.Id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<ScheduleExecutionHistory>> GetExecutionHistoryAsync(int scheduleId, int? limit = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var query = _context.ScheduleExecutionHistories
|
||||
.Where(h => h.ScheduleId == scheduleId)
|
||||
.OrderByDescending(h => h.StartTime);
|
||||
|
||||
if (limit.HasValue)
|
||||
{
|
||||
query = (IOrderedQueryable<ScheduleExecutionHistory>)query.Take(limit.Value);
|
||||
}
|
||||
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel recupero dello storico per schedulazione {ScheduleId}", scheduleId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<ScheduleExecutionHistory>> GetRecentExecutionsAsync(int limit = 50)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _context.ScheduleExecutionHistories
|
||||
.Include(h => h.Schedule)
|
||||
.OrderByDescending(h => h.StartTime)
|
||||
.Take(limit)
|
||||
.ToListAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel recupero delle esecuzioni recenti");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ScheduleExecutionHistory?> GetExecutionByIdAsync(int executionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _context.ScheduleExecutionHistories
|
||||
.Include(h => h.Schedule)
|
||||
.FirstOrDefaultAsync(h => h.Id == executionId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Errore nel recupero dell'esecuzione ID {ExecutionId}", executionId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user