134 lines
5.7 KiB
C#
134 lines
5.7 KiB
C#
|
|
using Microsoft.EntityFrameworkCore;
|
||
|
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||
|
|
using w4c_workflows.Models;
|
||
|
|
|
||
|
|
namespace w4c_workflows.Data;
|
||
|
|
|
||
|
|
public class WorkflowsDbContext : DbContext
|
||
|
|
{
|
||
|
|
public WorkflowsDbContext(DbContextOptions<WorkflowsDbContext> options) : base(options)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||
|
|
{
|
||
|
|
base.OnConfiguring(optionsBuilder);
|
||
|
|
|
||
|
|
// Additive schema (WorkflowTask.ArchivedAt for W7, WorkflowTask.Server for S8) is
|
||
|
|
// applied by idempotent raw SQL at startup — the project's house rule forbids new
|
||
|
|
// EF migrations. Suppress EF Core 10's "pending model changes" error so
|
||
|
|
// MigrateAsync (which only applies recorded migrations) does not reject those
|
||
|
|
// migration-less columns on a long-lived database. Runtime queries are unaffected.
|
||
|
|
optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
|
||
|
|
}
|
||
|
|
|
||
|
|
public DbSet<Workflow> Workflows => Set<Workflow>();
|
||
|
|
public DbSet<WorkflowTask> Tasks => Set<WorkflowTask>();
|
||
|
|
public DbSet<WorkflowRun> WorkflowRuns => Set<WorkflowRun>();
|
||
|
|
public DbSet<TaskRun> TaskRuns => Set<TaskRun>();
|
||
|
|
public DbSet<DurableState> DurableStates => Set<DurableState>();
|
||
|
|
public DbSet<ApiKey> ApiKeys => Set<ApiKey>();
|
||
|
|
|
||
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||
|
|
{
|
||
|
|
// Separate schema so the workflow tables never collide with the rest of
|
||
|
|
// the shared w4c database.
|
||
|
|
modelBuilder.HasDefaultSchema("workflows");
|
||
|
|
|
||
|
|
modelBuilder.Entity<Workflow>(e =>
|
||
|
|
{
|
||
|
|
e.HasKey(w => w.Id);
|
||
|
|
e.Property(w => w.Id).ValueGeneratedOnAdd();
|
||
|
|
e.Property(w => w.Name).HasMaxLength(200).IsRequired();
|
||
|
|
e.Property(w => w.Path).HasMaxLength(500).IsRequired();
|
||
|
|
e.Property(w => w.GitSha).HasMaxLength(64);
|
||
|
|
e.Property(w => w.Status).HasMaxLength(32).IsRequired();
|
||
|
|
e.Property(w => w.Mode).HasMaxLength(32).IsRequired();
|
||
|
|
e.Property(w => w.Target).HasMaxLength(100).IsRequired();
|
||
|
|
e.Property(w => w.TriggerJson).HasColumnType("jsonb");
|
||
|
|
e.HasIndex(w => new { w.TenantId, w.Name }).IsUnique();
|
||
|
|
e.HasIndex(w => w.TenantId);
|
||
|
|
});
|
||
|
|
|
||
|
|
modelBuilder.Entity<WorkflowTask>(e =>
|
||
|
|
{
|
||
|
|
e.HasKey(t => t.Id);
|
||
|
|
e.Property(t => t.Id).ValueGeneratedOnAdd();
|
||
|
|
e.Property(t => t.Key).HasMaxLength(100).IsRequired();
|
||
|
|
e.Property(t => t.Language).HasMaxLength(32).IsRequired();
|
||
|
|
e.Property(t => t.Mode).HasMaxLength(32).IsRequired();
|
||
|
|
e.Property(t => t.EntryJson).HasColumnType("jsonb");
|
||
|
|
e.Property(t => t.EnvJson).HasColumnType("jsonb");
|
||
|
|
e.HasOne(t => t.Workflow)
|
||
|
|
.WithMany(w => w.Tasks)
|
||
|
|
.HasForeignKey(t => t.WorkflowId)
|
||
|
|
.OnDelete(DeleteBehavior.Cascade);
|
||
|
|
e.HasIndex(t => t.WorkflowId);
|
||
|
|
|
||
|
|
// Preserve historical TaskRuns when a task is removed from the YAML:
|
||
|
|
// removed tasks are archived (ArchivedAt != null) rather than deleted, and
|
||
|
|
// every "active workflow definition" query excludes them automatically.
|
||
|
|
e.HasQueryFilter(t => t.ArchivedAt == null);
|
||
|
|
});
|
||
|
|
|
||
|
|
modelBuilder.Entity<WorkflowRun>(e =>
|
||
|
|
{
|
||
|
|
e.HasKey(r => r.Id);
|
||
|
|
e.Property(r => r.Id).ValueGeneratedOnAdd();
|
||
|
|
e.Property(r => r.Status).HasMaxLength(32).IsRequired();
|
||
|
|
e.Property(r => r.TriggerJson).HasColumnType("jsonb");
|
||
|
|
e.Property(r => r.InputJson).HasColumnType("jsonb");
|
||
|
|
e.Property(r => r.OutputJson).HasColumnType("jsonb");
|
||
|
|
e.Property(r => r.Error).HasColumnType("text");
|
||
|
|
e.Property(r => r.CorrelationId).HasMaxLength(128);
|
||
|
|
e.Property(r => r.StartTaskId);
|
||
|
|
e.Property(r => r.CompensationPlanJson).HasColumnType("jsonb");
|
||
|
|
e.HasOne(r => r.Workflow)
|
||
|
|
.WithMany()
|
||
|
|
.HasForeignKey(r => r.WorkflowId)
|
||
|
|
.OnDelete(DeleteBehavior.Restrict);
|
||
|
|
e.HasIndex(r => r.TenantId);
|
||
|
|
e.HasIndex(r => r.CorrelationId);
|
||
|
|
});
|
||
|
|
|
||
|
|
modelBuilder.Entity<TaskRun>(e =>
|
||
|
|
{
|
||
|
|
e.HasKey(t => t.Id);
|
||
|
|
e.Property(t => t.Id).ValueGeneratedOnAdd();
|
||
|
|
e.Property(t => t.Status).HasMaxLength(32).IsRequired();
|
||
|
|
e.Property(t => t.InputJson).HasColumnType("jsonb");
|
||
|
|
e.Property(t => t.OutputJson).HasColumnType("jsonb");
|
||
|
|
e.Property(t => t.Error).HasColumnType("text");
|
||
|
|
e.HasOne(t => t.Run)
|
||
|
|
.WithMany(r => r.TaskRuns)
|
||
|
|
.HasForeignKey(t => t.RunId)
|
||
|
|
.OnDelete(DeleteBehavior.Cascade);
|
||
|
|
e.HasOne(t => t.Task)
|
||
|
|
.WithMany()
|
||
|
|
.HasForeignKey(t => t.TaskId)
|
||
|
|
.OnDelete(DeleteBehavior.Restrict);
|
||
|
|
e.HasIndex(t => t.RunId);
|
||
|
|
});
|
||
|
|
|
||
|
|
modelBuilder.Entity<DurableState>(e =>
|
||
|
|
{
|
||
|
|
e.HasKey(d => d.InstanceId);
|
||
|
|
e.Property(d => d.InstanceId).HasMaxLength(128);
|
||
|
|
e.Property(d => d.StateJson).HasColumnType("jsonb");
|
||
|
|
e.Property(d => d.CorrelationId).HasMaxLength(128);
|
||
|
|
e.HasIndex(d => d.CorrelationId);
|
||
|
|
});
|
||
|
|
|
||
|
|
modelBuilder.Entity<ApiKey>(e =>
|
||
|
|
{
|
||
|
|
e.HasKey(k => k.Id);
|
||
|
|
e.Property(k => k.Id).ValueGeneratedOnAdd();
|
||
|
|
e.Property(k => k.TenantId).IsRequired();
|
||
|
|
e.Property(k => k.KeyHash).HasMaxLength(64).IsRequired();
|
||
|
|
e.Property(k => k.Label).HasMaxLength(200).IsRequired();
|
||
|
|
e.Property(k => k.ScopesJson).HasColumnType("jsonb");
|
||
|
|
e.HasIndex(k => k.TenantId);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|