wip: doctor/cli/docs/api to vector db consolidation; api hardening for descriptions, tenant, and scopes; migrations and conversions of all DALs to EF v10
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using StellaOps.Platform.Database.EfCore.Models;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
namespace StellaOps.Platform.Database.EfCore.CompiledModels
|
||||
{
|
||||
internal partial class ContextEnvironmentEntityType
|
||||
{
|
||||
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
|
||||
{
|
||||
var runtimeEntityType = model.AddEntityType(
|
||||
"StellaOps.Platform.Database.EfCore.Models.ContextEnvironment",
|
||||
typeof(ContextEnvironment),
|
||||
baseEntityType);
|
||||
|
||||
var environmentId = runtimeEntityType.AddProperty(
|
||||
"EnvironmentId",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(ContextEnvironment).GetProperty("EnvironmentId",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
environmentId.AddAnnotation("Relational:ColumnName", "environment_id");
|
||||
|
||||
var regionId = runtimeEntityType.AddProperty(
|
||||
"RegionId",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(ContextEnvironment).GetProperty("RegionId",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
regionId.AddAnnotation("Relational:ColumnName", "region_id");
|
||||
|
||||
var environmentType = runtimeEntityType.AddProperty(
|
||||
"EnvironmentType",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(ContextEnvironment).GetProperty("EnvironmentType",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
environmentType.AddAnnotation("Relational:ColumnName", "environment_type");
|
||||
|
||||
var displayName = runtimeEntityType.AddProperty(
|
||||
"DisplayName",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(ContextEnvironment).GetProperty("DisplayName",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
displayName.AddAnnotation("Relational:ColumnName", "display_name");
|
||||
|
||||
var sortOrder = runtimeEntityType.AddProperty(
|
||||
"SortOrder",
|
||||
typeof(int),
|
||||
propertyInfo: typeof(ContextEnvironment).GetProperty("SortOrder",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
sortOrder.AddAnnotation("Relational:ColumnName", "sort_order");
|
||||
|
||||
var enabled = runtimeEntityType.AddProperty(
|
||||
"Enabled",
|
||||
typeof(bool),
|
||||
propertyInfo: typeof(ContextEnvironment).GetProperty("Enabled",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
enabled.AddAnnotation("Relational:ColumnName", "enabled");
|
||||
enabled.AddAnnotation("Relational:DefaultValue", true);
|
||||
|
||||
var pk = runtimeEntityType.AddKey(new[] { environmentId });
|
||||
pk.AddAnnotation("Relational:Name", "context_environments_pkey");
|
||||
runtimeEntityType.SetPrimaryKey(pk);
|
||||
|
||||
runtimeEntityType.AddIndex(new[] { regionId, sortOrder, environmentId },
|
||||
"ix_platform_context_environments_region_sort");
|
||||
|
||||
runtimeEntityType.AddIndex(new[] { sortOrder, regionId, environmentId },
|
||||
"ix_platform_context_environments_sort");
|
||||
|
||||
return runtimeEntityType;
|
||||
}
|
||||
|
||||
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
|
||||
{
|
||||
runtimeEntityType.AddAnnotation("Relational:Schema", "platform");
|
||||
runtimeEntityType.AddAnnotation("Relational:TableName", "context_environments");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using StellaOps.Platform.Database.EfCore.Models;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
namespace StellaOps.Platform.Database.EfCore.CompiledModels
|
||||
{
|
||||
internal partial class ContextRegionEntityType
|
||||
{
|
||||
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
|
||||
{
|
||||
var runtimeEntityType = model.AddEntityType(
|
||||
"StellaOps.Platform.Database.EfCore.Models.ContextRegion",
|
||||
typeof(ContextRegion),
|
||||
baseEntityType);
|
||||
|
||||
var regionId = runtimeEntityType.AddProperty(
|
||||
"RegionId",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(ContextRegion).GetProperty("RegionId",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
regionId.AddAnnotation("Relational:ColumnName", "region_id");
|
||||
|
||||
var displayName = runtimeEntityType.AddProperty(
|
||||
"DisplayName",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(ContextRegion).GetProperty("DisplayName",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
displayName.AddAnnotation("Relational:ColumnName", "display_name");
|
||||
|
||||
var sortOrder = runtimeEntityType.AddProperty(
|
||||
"SortOrder",
|
||||
typeof(int),
|
||||
propertyInfo: typeof(ContextRegion).GetProperty("SortOrder",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
sortOrder.AddAnnotation("Relational:ColumnName", "sort_order");
|
||||
|
||||
var enabled = runtimeEntityType.AddProperty(
|
||||
"Enabled",
|
||||
typeof(bool),
|
||||
propertyInfo: typeof(ContextRegion).GetProperty("Enabled",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
enabled.AddAnnotation("Relational:ColumnName", "enabled");
|
||||
enabled.AddAnnotation("Relational:DefaultValue", true);
|
||||
|
||||
var pk = runtimeEntityType.AddKey(new[] { regionId });
|
||||
pk.AddAnnotation("Relational:Name", "context_regions_pkey");
|
||||
runtimeEntityType.SetPrimaryKey(pk);
|
||||
|
||||
runtimeEntityType.AddIndex(new[] { sortOrder, regionId },
|
||||
"ux_platform_context_regions_sort",
|
||||
unique: true);
|
||||
|
||||
return runtimeEntityType;
|
||||
}
|
||||
|
||||
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
|
||||
{
|
||||
runtimeEntityType.AddAnnotation("Relational:Schema", "platform");
|
||||
runtimeEntityType.AddAnnotation("Relational:TableName", "context_regions");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using StellaOps.Platform.Database.EfCore.Models;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
namespace StellaOps.Platform.Database.EfCore.CompiledModels
|
||||
{
|
||||
internal partial class EnvironmentSettingEntityType
|
||||
{
|
||||
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
|
||||
{
|
||||
var runtimeEntityType = model.AddEntityType(
|
||||
"StellaOps.Platform.Database.EfCore.Models.EnvironmentSetting",
|
||||
typeof(EnvironmentSetting),
|
||||
baseEntityType);
|
||||
|
||||
var key = runtimeEntityType.AddProperty(
|
||||
"Key",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(EnvironmentSetting).GetProperty("Key",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
key.AddAnnotation("Relational:ColumnName", "key");
|
||||
|
||||
var value = runtimeEntityType.AddProperty(
|
||||
"Value",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(EnvironmentSetting).GetProperty("Value",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
value.AddAnnotation("Relational:ColumnName", "value");
|
||||
|
||||
var updatedAt = runtimeEntityType.AddProperty(
|
||||
"UpdatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(EnvironmentSetting).GetProperty("UpdatedAt",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
updatedAt.AddAnnotation("Relational:ColumnName", "updated_at");
|
||||
updatedAt.AddAnnotation("Relational:DefaultValueSql", "now()");
|
||||
|
||||
var updatedBy = runtimeEntityType.AddProperty(
|
||||
"UpdatedBy",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(EnvironmentSetting).GetProperty("UpdatedBy",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
updatedBy.AddAnnotation("Relational:ColumnName", "updated_by");
|
||||
updatedBy.AddAnnotation("Relational:DefaultValueSql", "'system'");
|
||||
|
||||
var pk = runtimeEntityType.AddKey(new[] { key });
|
||||
pk.AddAnnotation("Relational:Name", "environment_settings_pkey");
|
||||
runtimeEntityType.SetPrimaryKey(pk);
|
||||
|
||||
return runtimeEntityType;
|
||||
}
|
||||
|
||||
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
|
||||
{
|
||||
runtimeEntityType.AddAnnotation("Relational:Schema", "platform");
|
||||
runtimeEntityType.AddAnnotation("Relational:TableName", "environment_settings");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using StellaOps.Platform.Database.EfCore.CompiledModels;
|
||||
using StellaOps.Platform.Database.EfCore.Context;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
[assembly: DbContextModel(typeof(PlatformDbContext), typeof(PlatformDbContextModel))]
|
||||
@@ -0,0 +1,48 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using StellaOps.Platform.Database.EfCore.Context;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
namespace StellaOps.Platform.Database.EfCore.CompiledModels
|
||||
{
|
||||
[DbContext(typeof(PlatformDbContext))]
|
||||
public partial class PlatformDbContextModel : RuntimeModel
|
||||
{
|
||||
private static readonly bool _useOldBehavior31751 =
|
||||
System.AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31751", out var enabled31751) && enabled31751;
|
||||
|
||||
static PlatformDbContextModel()
|
||||
{
|
||||
var model = new PlatformDbContextModel();
|
||||
|
||||
if (_useOldBehavior31751)
|
||||
{
|
||||
model.Initialize();
|
||||
}
|
||||
else
|
||||
{
|
||||
var thread = new System.Threading.Thread(RunInitialization, 10 * 1024 * 1024);
|
||||
thread.Start();
|
||||
thread.Join();
|
||||
|
||||
void RunInitialization()
|
||||
{
|
||||
model.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
model.Customize();
|
||||
_instance = (PlatformDbContextModel)model.FinalizeModel();
|
||||
}
|
||||
|
||||
private static PlatformDbContextModel _instance;
|
||||
public static IModel Instance => _instance;
|
||||
|
||||
partial void Initialize();
|
||||
|
||||
partial void Customize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
namespace StellaOps.Platform.Database.EfCore.CompiledModels
|
||||
{
|
||||
public partial class PlatformDbContextModel
|
||||
{
|
||||
private PlatformDbContextModel()
|
||||
: base(skipDetectChanges: false, modelId: new Guid("b2a4e6c8-1d3f-4a5b-9c7e-0f8a2b4d6e10"), entityTypeCount: 4)
|
||||
{
|
||||
}
|
||||
|
||||
partial void Initialize()
|
||||
{
|
||||
var environmentSetting = EnvironmentSettingEntityType.Create(this);
|
||||
var contextRegion = ContextRegionEntityType.Create(this);
|
||||
var contextEnvironment = ContextEnvironmentEntityType.Create(this);
|
||||
var uiContextPreference = UiContextPreferenceEntityType.Create(this);
|
||||
|
||||
EnvironmentSettingEntityType.CreateAnnotations(environmentSetting);
|
||||
ContextRegionEntityType.CreateAnnotations(contextRegion);
|
||||
ContextEnvironmentEntityType.CreateAnnotations(contextEnvironment);
|
||||
UiContextPreferenceEntityType.CreateAnnotations(uiContextPreference);
|
||||
|
||||
AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
AddAnnotation("ProductVersion", "10.0.0");
|
||||
AddAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using StellaOps.Platform.Database.EfCore.Models;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
namespace StellaOps.Platform.Database.EfCore.CompiledModels
|
||||
{
|
||||
internal partial class UiContextPreferenceEntityType
|
||||
{
|
||||
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
|
||||
{
|
||||
var runtimeEntityType = model.AddEntityType(
|
||||
"StellaOps.Platform.Database.EfCore.Models.UiContextPreference",
|
||||
typeof(UiContextPreference),
|
||||
baseEntityType);
|
||||
|
||||
var tenantId = runtimeEntityType.AddProperty(
|
||||
"TenantId",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(UiContextPreference).GetProperty("TenantId",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
tenantId.AddAnnotation("Relational:ColumnName", "tenant_id");
|
||||
|
||||
var actorId = runtimeEntityType.AddProperty(
|
||||
"ActorId",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(UiContextPreference).GetProperty("ActorId",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
actorId.AddAnnotation("Relational:ColumnName", "actor_id");
|
||||
|
||||
var regions = runtimeEntityType.AddProperty(
|
||||
"Regions",
|
||||
typeof(string[]),
|
||||
propertyInfo: typeof(UiContextPreference).GetProperty("Regions",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
regions.AddAnnotation("Relational:ColumnName", "regions");
|
||||
regions.AddAnnotation("Relational:DefaultValueSql", "ARRAY[]::text[]");
|
||||
|
||||
var environments = runtimeEntityType.AddProperty(
|
||||
"Environments",
|
||||
typeof(string[]),
|
||||
propertyInfo: typeof(UiContextPreference).GetProperty("Environments",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
environments.AddAnnotation("Relational:ColumnName", "environments");
|
||||
environments.AddAnnotation("Relational:DefaultValueSql", "ARRAY[]::text[]");
|
||||
|
||||
var timeWindow = runtimeEntityType.AddProperty(
|
||||
"TimeWindow",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(UiContextPreference).GetProperty("TimeWindow",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
timeWindow.AddAnnotation("Relational:ColumnName", "time_window");
|
||||
timeWindow.AddAnnotation("Relational:DefaultValueSql", "'24h'");
|
||||
|
||||
var updatedAt = runtimeEntityType.AddProperty(
|
||||
"UpdatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(UiContextPreference).GetProperty("UpdatedAt",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
updatedAt.AddAnnotation("Relational:ColumnName", "updated_at");
|
||||
updatedAt.AddAnnotation("Relational:DefaultValueSql", "now()");
|
||||
|
||||
var updatedBy = runtimeEntityType.AddProperty(
|
||||
"UpdatedBy",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(UiContextPreference).GetProperty("UpdatedBy",
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: null);
|
||||
updatedBy.AddAnnotation("Relational:ColumnName", "updated_by");
|
||||
updatedBy.AddAnnotation("Relational:DefaultValueSql", "'system'");
|
||||
|
||||
var pk = runtimeEntityType.AddKey(new[] { tenantId, actorId });
|
||||
pk.AddAnnotation("Relational:Name", "ui_context_preferences_pkey");
|
||||
runtimeEntityType.SetPrimaryKey(pk);
|
||||
|
||||
runtimeEntityType.AddIndex(new[] { updatedAt, tenantId, actorId },
|
||||
"ix_platform_ui_context_preferences_updated");
|
||||
|
||||
return runtimeEntityType;
|
||||
}
|
||||
|
||||
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
|
||||
{
|
||||
runtimeEntityType.AddAnnotation("Relational:Schema", "platform");
|
||||
runtimeEntityType.AddAnnotation("Relational:TableName", "ui_context_preferences");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using StellaOps.Platform.Database.EfCore.Models;
|
||||
|
||||
namespace StellaOps.Platform.Database.EfCore.Context;
|
||||
|
||||
public partial class PlatformDbContext : DbContext
|
||||
{
|
||||
private readonly string _schemaName;
|
||||
|
||||
public PlatformDbContext(DbContextOptions<PlatformDbContext> options, string? schemaName = null)
|
||||
: base(options)
|
||||
{
|
||||
_schemaName = string.IsNullOrWhiteSpace(schemaName)
|
||||
? "platform"
|
||||
: schemaName.Trim();
|
||||
}
|
||||
|
||||
public virtual DbSet<EnvironmentSetting> EnvironmentSettings { get; set; }
|
||||
|
||||
public virtual DbSet<ContextRegion> ContextRegions { get; set; }
|
||||
|
||||
public virtual DbSet<ContextEnvironment> ContextEnvironments { get; set; }
|
||||
|
||||
public virtual DbSet<UiContextPreference> UiContextPreferences { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
var schemaName = _schemaName;
|
||||
|
||||
modelBuilder.Entity<EnvironmentSetting>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Key).HasName("environment_settings_pkey");
|
||||
|
||||
entity.ToTable("environment_settings", schemaName);
|
||||
|
||||
entity.Property(e => e.Key).HasColumnName("key");
|
||||
entity.Property(e => e.Value).HasColumnName("value");
|
||||
entity.Property(e => e.UpdatedAt)
|
||||
.HasDefaultValueSql("now()")
|
||||
.HasColumnName("updated_at");
|
||||
entity.Property(e => e.UpdatedBy)
|
||||
.HasDefaultValueSql("'system'")
|
||||
.HasColumnName("updated_by");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ContextRegion>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.RegionId).HasName("context_regions_pkey");
|
||||
|
||||
entity.ToTable("context_regions", schemaName);
|
||||
|
||||
entity.HasIndex(e => new { e.SortOrder, e.RegionId }, "ux_platform_context_regions_sort")
|
||||
.IsUnique();
|
||||
|
||||
entity.Property(e => e.RegionId).HasColumnName("region_id");
|
||||
entity.Property(e => e.DisplayName).HasColumnName("display_name");
|
||||
entity.Property(e => e.SortOrder).HasColumnName("sort_order");
|
||||
entity.Property(e => e.Enabled)
|
||||
.HasDefaultValue(true)
|
||||
.HasColumnName("enabled");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ContextEnvironment>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.EnvironmentId).HasName("context_environments_pkey");
|
||||
|
||||
entity.ToTable("context_environments", schemaName);
|
||||
|
||||
entity.HasIndex(e => new { e.RegionId, e.SortOrder, e.EnvironmentId },
|
||||
"ix_platform_context_environments_region_sort");
|
||||
|
||||
entity.HasIndex(e => new { e.SortOrder, e.RegionId, e.EnvironmentId },
|
||||
"ix_platform_context_environments_sort");
|
||||
|
||||
entity.Property(e => e.EnvironmentId).HasColumnName("environment_id");
|
||||
entity.Property(e => e.RegionId).HasColumnName("region_id");
|
||||
entity.Property(e => e.EnvironmentType).HasColumnName("environment_type");
|
||||
entity.Property(e => e.DisplayName).HasColumnName("display_name");
|
||||
entity.Property(e => e.SortOrder).HasColumnName("sort_order");
|
||||
entity.Property(e => e.Enabled)
|
||||
.HasDefaultValue(true)
|
||||
.HasColumnName("enabled");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<UiContextPreference>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.TenantId, e.ActorId }).HasName("ui_context_preferences_pkey");
|
||||
|
||||
entity.ToTable("ui_context_preferences", schemaName);
|
||||
|
||||
entity.HasIndex(e => new { e.UpdatedAt, e.TenantId, e.ActorId },
|
||||
"ix_platform_ui_context_preferences_updated")
|
||||
.IsDescending(true, false, false);
|
||||
|
||||
entity.Property(e => e.TenantId).HasColumnName("tenant_id");
|
||||
entity.Property(e => e.ActorId).HasColumnName("actor_id");
|
||||
entity.Property(e => e.Regions)
|
||||
.HasDefaultValueSql("ARRAY[]::text[]")
|
||||
.HasColumnName("regions");
|
||||
entity.Property(e => e.Environments)
|
||||
.HasDefaultValueSql("ARRAY[]::text[]")
|
||||
.HasColumnName("environments");
|
||||
entity.Property(e => e.TimeWindow)
|
||||
.HasDefaultValueSql("'24h'")
|
||||
.HasColumnName("time_window");
|
||||
entity.Property(e => e.UpdatedAt)
|
||||
.HasDefaultValueSql("now()")
|
||||
.HasColumnName("updated_at");
|
||||
entity.Property(e => e.UpdatedBy)
|
||||
.HasDefaultValueSql("'system'")
|
||||
.HasColumnName("updated_by");
|
||||
});
|
||||
|
||||
OnModelCreatingPartial(modelBuilder);
|
||||
}
|
||||
|
||||
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace StellaOps.Platform.Database.EfCore.Context;
|
||||
|
||||
public sealed class PlatformDesignTimeDbContextFactory : IDesignTimeDbContextFactory<PlatformDbContext>
|
||||
{
|
||||
private const string DefaultConnectionString = "Host=localhost;Port=55434;Database=postgres;Username=postgres;Password=postgres;Search Path=platform,public";
|
||||
private const string ConnectionStringEnvironmentVariable = "STELLAOPS_PLATFORM_EF_CONNECTION";
|
||||
|
||||
public PlatformDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var connectionString = ResolveConnectionString();
|
||||
var options = new DbContextOptionsBuilder<PlatformDbContext>()
|
||||
.UseNpgsql(connectionString)
|
||||
.Options;
|
||||
|
||||
return new PlatformDbContext(options);
|
||||
}
|
||||
|
||||
private static string ResolveConnectionString()
|
||||
{
|
||||
var fromEnvironment = Environment.GetEnvironmentVariable(ConnectionStringEnvironmentVariable);
|
||||
return string.IsNullOrWhiteSpace(fromEnvironment) ? DefaultConnectionString : fromEnvironment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace StellaOps.Platform.Database.EfCore.Models;
|
||||
|
||||
public partial class ContextEnvironment
|
||||
{
|
||||
public string EnvironmentId { get; set; } = null!;
|
||||
|
||||
public string RegionId { get; set; } = null!;
|
||||
|
||||
public string EnvironmentType { get; set; } = null!;
|
||||
|
||||
public string DisplayName { get; set; } = null!;
|
||||
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace StellaOps.Platform.Database.EfCore.Models;
|
||||
|
||||
public partial class ContextRegion
|
||||
{
|
||||
public string RegionId { get; set; } = null!;
|
||||
|
||||
public string DisplayName { get; set; } = null!;
|
||||
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace StellaOps.Platform.Database.EfCore.Models;
|
||||
|
||||
public partial class EnvironmentSetting
|
||||
{
|
||||
public string Key { get; set; } = null!;
|
||||
|
||||
public string Value { get; set; } = null!;
|
||||
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
public string UpdatedBy { get; set; } = null!;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace StellaOps.Platform.Database.EfCore.Models;
|
||||
|
||||
public partial class UiContextPreference
|
||||
{
|
||||
public string TenantId { get; set; } = null!;
|
||||
|
||||
public string ActorId { get; set; } = null!;
|
||||
|
||||
public string[] Regions { get; set; } = [];
|
||||
|
||||
public string[] Environments { get; set; } = [];
|
||||
|
||||
public string TimeWindow { get; set; } = null!;
|
||||
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
public string UpdatedBy { get; set; } = null!;
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
using StellaOps.Infrastructure.Postgres.Migrations;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace StellaOps.Platform.Database;
|
||||
|
||||
/// <summary>
|
||||
/// Consolidated migration artifact generated from all configured sources of a module.
|
||||
/// </summary>
|
||||
public sealed record MigrationModuleConsolidatedArtifact(
|
||||
string MigrationName,
|
||||
string Script,
|
||||
string Checksum,
|
||||
IReadOnlyList<MigrationModuleConsolidatedSourceMigration> SourceMigrations);
|
||||
|
||||
/// <summary>
|
||||
/// Source migration metadata retained for compatibility backfill.
|
||||
/// </summary>
|
||||
public sealed record MigrationModuleConsolidatedSourceMigration(
|
||||
string Name,
|
||||
MigrationCategory Category,
|
||||
string Checksum,
|
||||
string Content,
|
||||
string SourceResourceName);
|
||||
|
||||
/// <summary>
|
||||
/// Builds deterministic consolidated migration scripts per service module.
|
||||
/// </summary>
|
||||
public static class MigrationModuleConsolidation
|
||||
{
|
||||
public static MigrationModuleConsolidatedArtifact Build(MigrationModuleInfo module)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(module);
|
||||
|
||||
var migrationsByName = new Dictionary<string, MigrationModuleConsolidatedSourceMigration>(StringComparer.Ordinal);
|
||||
foreach (var source in module.Sources)
|
||||
{
|
||||
foreach (var migration in LoadMigrationsFromSource(source))
|
||||
{
|
||||
if (migrationsByName.TryGetValue(migration.Name, out var existing))
|
||||
{
|
||||
if (!string.Equals(existing.Checksum, migration.Checksum, StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Duplicate migration name '{migration.Name}' with different content discovered while consolidating module '{module.Name}'.");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
migrationsByName[migration.Name] = migration;
|
||||
}
|
||||
}
|
||||
|
||||
if (migrationsByName.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Module '{module.Name}' has no migration resources to consolidate.");
|
||||
}
|
||||
|
||||
var sourceMigrations = migrationsByName.Values
|
||||
.OrderBy(static migration => migration.Name, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
var script = BuildConsolidatedScript(module, sourceMigrations);
|
||||
var checksum = ComputeChecksum(script);
|
||||
var migrationName = $"100_consolidated_{NormalizeModuleName(module.Name)}.sql";
|
||||
|
||||
return new MigrationModuleConsolidatedArtifact(
|
||||
migrationName,
|
||||
script,
|
||||
checksum,
|
||||
sourceMigrations);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<MigrationModuleConsolidatedSourceMigration> LoadMigrationsFromSource(
|
||||
MigrationModuleSourceInfo source)
|
||||
{
|
||||
var resources = source.MigrationsAssembly
|
||||
.GetManifestResourceNames()
|
||||
.Where(static name => name.EndsWith(".sql", StringComparison.OrdinalIgnoreCase))
|
||||
.Where(name =>
|
||||
string.IsNullOrWhiteSpace(source.ResourcePrefix) ||
|
||||
name.Contains(source.ResourcePrefix, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(static name => name, StringComparer.Ordinal);
|
||||
|
||||
var migrations = new List<MigrationModuleConsolidatedSourceMigration>();
|
||||
foreach (var resourceName in resources)
|
||||
{
|
||||
using var stream = source.MigrationsAssembly.GetManifestResourceStream(resourceName);
|
||||
if (stream is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
using var reader = new StreamReader(stream);
|
||||
var content = reader.ReadToEnd();
|
||||
var fileName = ExtractFileName(resourceName);
|
||||
var category = MigrationCategoryExtensions.GetCategory(fileName);
|
||||
var checksum = ComputeChecksum(content);
|
||||
|
||||
migrations.Add(new MigrationModuleConsolidatedSourceMigration(
|
||||
Name: fileName,
|
||||
Category: category,
|
||||
Checksum: checksum,
|
||||
Content: content,
|
||||
SourceResourceName: resourceName));
|
||||
}
|
||||
|
||||
return migrations;
|
||||
}
|
||||
|
||||
private static string BuildConsolidatedScript(
|
||||
MigrationModuleInfo module,
|
||||
IReadOnlyList<MigrationModuleConsolidatedSourceMigration> sourceMigrations)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append("-- Consolidated migration for module '");
|
||||
builder.Append(module.Name);
|
||||
builder.AppendLine("'.");
|
||||
builder.Append("-- Generated deterministically from ");
|
||||
builder.Append(sourceMigrations.Count);
|
||||
builder.AppendLine(" source migrations.");
|
||||
builder.AppendLine();
|
||||
|
||||
foreach (var migration in sourceMigrations)
|
||||
{
|
||||
builder.Append("-- BEGIN ");
|
||||
builder.AppendLine(migration.SourceResourceName);
|
||||
builder.AppendLine(migration.Content.TrimEnd());
|
||||
builder.Append("-- END ");
|
||||
builder.AppendLine(migration.SourceResourceName);
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string ExtractFileName(string resourceName)
|
||||
{
|
||||
var lastSlash = resourceName.LastIndexOf('/');
|
||||
if (lastSlash >= 0)
|
||||
{
|
||||
return resourceName[(lastSlash + 1)..];
|
||||
}
|
||||
|
||||
var parts = resourceName.Split('.');
|
||||
for (var i = parts.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (parts[i].EndsWith("sql", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return i > 0 ? $"{parts[i - 1]}.sql" : parts[i];
|
||||
}
|
||||
}
|
||||
|
||||
return resourceName;
|
||||
}
|
||||
|
||||
private static string ComputeChecksum(string content)
|
||||
{
|
||||
var normalized = content.Replace("\r\n", "\n", StringComparison.Ordinal)
|
||||
.Replace("\r", "\n", StringComparison.Ordinal);
|
||||
var bytes = Encoding.UTF8.GetBytes(normalized);
|
||||
var hash = SHA256.HashData(bytes);
|
||||
return Convert.ToHexStringLower(hash);
|
||||
}
|
||||
|
||||
private static string NormalizeModuleName(string moduleName)
|
||||
{
|
||||
var chars = moduleName.Where(char.IsLetterOrDigit)
|
||||
.Select(char.ToLowerInvariant)
|
||||
.ToArray();
|
||||
return chars.Length == 0 ? "module" : new string(chars);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,23 @@ internal static class MigrationModulePluginDiscovery
|
||||
$"Invalid migration module plugin '{plugin.GetType().FullName}': schema name is required.");
|
||||
}
|
||||
|
||||
if (module.Sources.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Invalid migration module plugin '{plugin.GetType().FullName}': at least one migration source is required.");
|
||||
}
|
||||
|
||||
var sourceSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var source in module.Sources)
|
||||
{
|
||||
var sourceIdentity = $"{source.MigrationsAssembly.FullName}|{source.ResourcePrefix}";
|
||||
if (!sourceSet.Add(sourceIdentity))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Invalid migration module plugin '{plugin.GetType().FullName}': duplicate migration source '{sourceIdentity}' for module '{module.Name}'.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!modulesByName.TryAdd(module.Name, module))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
@@ -161,4 +178,3 @@ internal static class MigrationModulePluginDiscovery
|
||||
return directories.OrderBy(static directory => directory, StringComparer.Ordinal).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,100 +1,296 @@
|
||||
using StellaOps.AdvisoryAI.Storage.Postgres;
|
||||
using StellaOps.Attestor.Persistence;
|
||||
using StellaOps.Eventing.Postgres;
|
||||
using StellaOps.AirGap.Persistence.Postgres;
|
||||
using StellaOps.BinaryIndex.GoldenSet;
|
||||
using StellaOps.BinaryIndex.Persistence;
|
||||
using StellaOps.EvidenceLocker.Infrastructure.Db;
|
||||
using StellaOps.Artifact.Infrastructure;
|
||||
using StellaOps.Authority.Persistence.Postgres;
|
||||
using StellaOps.Concelier.Persistence.Postgres;
|
||||
using StellaOps.Evidence.Persistence.Postgres;
|
||||
using StellaOps.Excititor.Persistence.Postgres;
|
||||
using StellaOps.Notify.Persistence.Postgres;
|
||||
using StellaOps.Plugin.Registry;
|
||||
using StellaOps.Policy.Persistence.Postgres;
|
||||
using StellaOps.ReachGraph.Persistence.Postgres;
|
||||
using StellaOps.Remediation.Persistence.Postgres;
|
||||
using StellaOps.SbomService.Lineage.Persistence;
|
||||
using StellaOps.Scanner.Storage.Postgres;
|
||||
using StellaOps.Scanner.Triage;
|
||||
using StellaOps.Scheduler.Persistence.Postgres;
|
||||
using StellaOps.Timeline.Core.Postgres;
|
||||
using StellaOps.TimelineIndexer.Infrastructure;
|
||||
using StellaOps.Verdict.Persistence.Postgres;
|
||||
using StellaOps.Signals.Persistence.Postgres;
|
||||
using StellaOps.Graph.Indexer.Persistence.Postgres;
|
||||
using StellaOps.Unknowns.Persistence.Postgres;
|
||||
using StellaOps.VexHub.Persistence.Postgres;
|
||||
using StellaOps.VexLens.Persistence.Postgres;
|
||||
using StellaOps.Findings.Ledger.Infrastructure.Postgres;
|
||||
using StellaOps.Orchestrator.Infrastructure.Postgres;
|
||||
|
||||
namespace StellaOps.Platform.Database;
|
||||
|
||||
public sealed class AdvisoryAiMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "AdvisoryAI",
|
||||
schemaName: "advisoryai",
|
||||
migrationsAssembly: typeof(AdvisoryAiDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class AirGapMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
Name: "AirGap",
|
||||
SchemaName: "airgap",
|
||||
MigrationsAssembly: typeof(AirGapDataSource).Assembly);
|
||||
name: "AirGap",
|
||||
schemaName: "airgap",
|
||||
migrationsAssembly: typeof(AirGapDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class AttestorMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "Attestor",
|
||||
schemaName: "proofchain",
|
||||
migrationsAssembly: typeof(ProofChainDbContext).Assembly,
|
||||
resourcePrefix: "StellaOps.Attestor.Persistence.Migrations");
|
||||
}
|
||||
|
||||
public sealed class BinaryIndexMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "BinaryIndex",
|
||||
schemaName: "binaries",
|
||||
sources:
|
||||
[
|
||||
new MigrationModuleSourceInfo(
|
||||
typeof(BinaryIndexMigrationRunner).Assembly,
|
||||
"StellaOps.BinaryIndex.Persistence.Migrations"),
|
||||
new MigrationModuleSourceInfo(
|
||||
typeof(PostgresGoldenSetStore).Assembly,
|
||||
"StellaOps.BinaryIndex.GoldenSet.Migrations")
|
||||
]);
|
||||
}
|
||||
|
||||
public sealed class AuthorityMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
Name: "Authority",
|
||||
SchemaName: "authority",
|
||||
MigrationsAssembly: typeof(AuthorityDataSource).Assembly,
|
||||
ResourcePrefix: "StellaOps.Authority.Persistence.Migrations");
|
||||
name: "Authority",
|
||||
schemaName: "authority",
|
||||
migrationsAssembly: typeof(AuthorityDataSource).Assembly,
|
||||
resourcePrefix: "StellaOps.Authority.Persistence.Migrations");
|
||||
}
|
||||
|
||||
public sealed class EventingMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "Eventing",
|
||||
schemaName: "timeline",
|
||||
migrationsAssembly: typeof(EventingDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class GraphMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "Graph",
|
||||
schemaName: "graph",
|
||||
sources:
|
||||
[
|
||||
new MigrationModuleSourceInfo(typeof(GraphIndexerDataSource).Assembly)
|
||||
]);
|
||||
}
|
||||
|
||||
public sealed class EvidenceMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "Evidence",
|
||||
schemaName: "evidence",
|
||||
sources:
|
||||
[
|
||||
new MigrationModuleSourceInfo(typeof(EvidenceDataSource).Assembly),
|
||||
new MigrationModuleSourceInfo(typeof(ArtifactDataSource).Assembly)
|
||||
]);
|
||||
}
|
||||
|
||||
public sealed class EvidenceLockerMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "EvidenceLocker",
|
||||
schemaName: "evidence_locker",
|
||||
migrationsAssembly: typeof(EvidenceLockerDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class SchedulerMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
Name: "Scheduler",
|
||||
SchemaName: "scheduler",
|
||||
MigrationsAssembly: typeof(SchedulerDataSource).Assembly,
|
||||
ResourcePrefix: "StellaOps.Scheduler.Persistence.Migrations");
|
||||
name: "Scheduler",
|
||||
schemaName: "scheduler",
|
||||
migrationsAssembly: typeof(SchedulerDataSource).Assembly,
|
||||
resourcePrefix: "StellaOps.Scheduler.Persistence.Migrations");
|
||||
}
|
||||
|
||||
public sealed class ConcelierMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
Name: "Concelier",
|
||||
SchemaName: "vuln",
|
||||
MigrationsAssembly: typeof(ConcelierDataSource).Assembly,
|
||||
ResourcePrefix: "StellaOps.Concelier.Persistence.Migrations");
|
||||
name: "Concelier",
|
||||
schemaName: "vuln",
|
||||
migrationsAssembly: typeof(ConcelierDataSource).Assembly,
|
||||
resourcePrefix: "StellaOps.Concelier.Persistence.Migrations");
|
||||
}
|
||||
|
||||
public sealed class PolicyMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
Name: "Policy",
|
||||
SchemaName: "policy",
|
||||
MigrationsAssembly: typeof(PolicyDataSource).Assembly,
|
||||
ResourcePrefix: "StellaOps.Policy.Persistence.Migrations");
|
||||
name: "Policy",
|
||||
schemaName: "policy",
|
||||
migrationsAssembly: typeof(PolicyDataSource).Assembly,
|
||||
resourcePrefix: "StellaOps.Policy.Persistence.Migrations");
|
||||
}
|
||||
|
||||
public sealed class NotifyMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
Name: "Notify",
|
||||
SchemaName: "notify",
|
||||
MigrationsAssembly: typeof(NotifyDataSource).Assembly,
|
||||
ResourcePrefix: "StellaOps.Notify.Persistence.Migrations");
|
||||
name: "Notify",
|
||||
schemaName: "notify",
|
||||
migrationsAssembly: typeof(NotifyDataSource).Assembly,
|
||||
resourcePrefix: "StellaOps.Notify.Persistence.Migrations");
|
||||
}
|
||||
|
||||
public sealed class ExcititorMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
Name: "Excititor",
|
||||
SchemaName: "vex",
|
||||
MigrationsAssembly: typeof(ExcititorDataSource).Assembly,
|
||||
ResourcePrefix: "StellaOps.Excititor.Persistence.Migrations");
|
||||
name: "Excititor",
|
||||
schemaName: "vex",
|
||||
migrationsAssembly: typeof(ExcititorDataSource).Assembly,
|
||||
resourcePrefix: "StellaOps.Excititor.Persistence.Migrations");
|
||||
}
|
||||
|
||||
public sealed class PluginRegistryMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "PluginRegistry",
|
||||
schemaName: "platform",
|
||||
migrationsAssembly: typeof(PluginRegistryMigrationRunner).Assembly,
|
||||
resourcePrefix: "StellaOps.Plugin.Registry.Migrations");
|
||||
}
|
||||
|
||||
public sealed class PlatformMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
Name: "Platform",
|
||||
SchemaName: "release",
|
||||
MigrationsAssembly: typeof(ReleaseMigrationRunner).Assembly,
|
||||
ResourcePrefix: "StellaOps.Platform.Database.Migrations.Release");
|
||||
name: "Platform",
|
||||
schemaName: "release",
|
||||
migrationsAssembly: typeof(ReleaseMigrationRunner).Assembly,
|
||||
resourcePrefix: "StellaOps.Platform.Database.Migrations.Release");
|
||||
}
|
||||
|
||||
public sealed class ScannerMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
Name: "Scanner",
|
||||
SchemaName: "scanner",
|
||||
MigrationsAssembly: typeof(ScannerDataSource).Assembly);
|
||||
name: "Scanner",
|
||||
schemaName: "scanner",
|
||||
sources:
|
||||
[
|
||||
new MigrationModuleSourceInfo(typeof(ScannerDataSource).Assembly),
|
||||
new MigrationModuleSourceInfo(
|
||||
typeof(TriageDbContext).Assembly,
|
||||
"StellaOps.Scanner.Triage.Migrations")
|
||||
]);
|
||||
}
|
||||
|
||||
public sealed class SignalsMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "Signals",
|
||||
schemaName: "signals",
|
||||
migrationsAssembly: typeof(SignalsDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class TimelineIndexerMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
Name: "TimelineIndexer",
|
||||
SchemaName: "timeline",
|
||||
MigrationsAssembly: typeof(TimelineIndexerDataSource).Assembly,
|
||||
ResourcePrefix: "StellaOps.TimelineIndexer.Infrastructure.Db.Migrations");
|
||||
name: "TimelineIndexer",
|
||||
schemaName: "timeline",
|
||||
sources:
|
||||
[
|
||||
new MigrationModuleSourceInfo(
|
||||
typeof(TimelineIndexerDataSource).Assembly,
|
||||
"StellaOps.TimelineIndexer.Infrastructure.Db.Migrations"),
|
||||
new MigrationModuleSourceInfo(
|
||||
typeof(TimelineCoreDataSource).Assembly)
|
||||
]);
|
||||
}
|
||||
|
||||
public sealed class VexHubMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "VexHub",
|
||||
schemaName: "vexhub",
|
||||
migrationsAssembly: typeof(VexHubDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class RemediationMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "Remediation",
|
||||
schemaName: "remediation",
|
||||
migrationsAssembly: typeof(RemediationDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class VexLensMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "VexLens",
|
||||
schemaName: "vexlens",
|
||||
migrationsAssembly: typeof(VexLensDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class SbomLineageMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "SbomLineage",
|
||||
schemaName: "sbom",
|
||||
migrationsAssembly: typeof(LineageDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class ReachGraphMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "ReachGraph",
|
||||
schemaName: "reachgraph",
|
||||
migrationsAssembly: typeof(ReachGraphDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class UnknownsMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "Unknowns",
|
||||
schemaName: "unknowns",
|
||||
migrationsAssembly: typeof(UnknownsDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class VerdictMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "Verdict",
|
||||
schemaName: "stellaops",
|
||||
migrationsAssembly: typeof(VerdictDataSource).Assembly,
|
||||
resourcePrefix: "StellaOps.Verdict.Persistence.Migrations");
|
||||
}
|
||||
|
||||
public sealed class OrchestratorMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "Orchestrator",
|
||||
schemaName: "orchestrator",
|
||||
migrationsAssembly: typeof(OrchestratorDataSource).Assembly);
|
||||
}
|
||||
|
||||
public sealed class FindingsLedgerMigrationModulePlugin : IMigrationModulePlugin
|
||||
{
|
||||
public MigrationModuleInfo Module { get; } = new(
|
||||
name: "FindingsLedger",
|
||||
schemaName: "public",
|
||||
migrationsAssembly: typeof(LedgerDataSource).Assembly,
|
||||
resourcePrefix: "StellaOps.Findings.Ledger.migrations");
|
||||
}
|
||||
|
||||
@@ -4,14 +4,62 @@ using System.Threading;
|
||||
namespace StellaOps.Platform.Database;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a PostgreSQL module with migration metadata.
|
||||
/// Defines one migration source (assembly + optional resource prefix) for a service module.
|
||||
/// </summary>
|
||||
public sealed record MigrationModuleInfo(
|
||||
string Name,
|
||||
string SchemaName,
|
||||
public sealed record MigrationModuleSourceInfo(
|
||||
Assembly MigrationsAssembly,
|
||||
string? ResourcePrefix = null);
|
||||
|
||||
/// <summary>
|
||||
/// Defines a PostgreSQL module with migration metadata.
|
||||
/// </summary>
|
||||
public sealed record MigrationModuleInfo
|
||||
{
|
||||
public MigrationModuleInfo(
|
||||
string name,
|
||||
string schemaName,
|
||||
Assembly migrationsAssembly,
|
||||
string? resourcePrefix = null)
|
||||
: this(
|
||||
name,
|
||||
schemaName,
|
||||
[new MigrationModuleSourceInfo(migrationsAssembly, resourcePrefix)])
|
||||
{
|
||||
}
|
||||
|
||||
public MigrationModuleInfo(
|
||||
string name,
|
||||
string schemaName,
|
||||
IReadOnlyList<MigrationModuleSourceInfo> sources)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(name);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(schemaName);
|
||||
ArgumentNullException.ThrowIfNull(sources);
|
||||
|
||||
if (sources.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("At least one migration source is required.", nameof(sources));
|
||||
}
|
||||
|
||||
if (sources.Any(static source => source.MigrationsAssembly is null))
|
||||
{
|
||||
throw new ArgumentException("Migration source assembly cannot be null.", nameof(sources));
|
||||
}
|
||||
|
||||
Name = name;
|
||||
SchemaName = schemaName;
|
||||
Sources = sources.ToArray();
|
||||
MigrationsAssembly = Sources[0].MigrationsAssembly;
|
||||
ResourcePrefix = Sources[0].ResourcePrefix;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string SchemaName { get; }
|
||||
public Assembly MigrationsAssembly { get; }
|
||||
public string? ResourcePrefix { get; }
|
||||
public IReadOnlyList<MigrationModuleSourceInfo> Sources { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Canonical PostgreSQL migration module registry owned by Platform.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
-- Release schema prerequisite for tenant fallback lookups.
|
||||
-- Keeps clean-install migration execution independent from optional shared-schema owners.
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS shared;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS shared.tenants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
is_default BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_shared_tenants_single_default
|
||||
ON shared.tenants (is_default)
|
||||
WHERE is_default;
|
||||
@@ -151,10 +151,11 @@ CREATE TABLE IF NOT EXISTS release.release_tags (
|
||||
release_id UUID NOT NULL REFERENCES release.releases(id) ON DELETE CASCADE,
|
||||
environment_id UUID REFERENCES release.environments(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
created_by UUID NOT NULL,
|
||||
PRIMARY KEY (tenant_id, tag, COALESCE(environment_id, '00000000-0000-0000-0000-000000000000'::UUID))
|
||||
created_by UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_release_tags_tenant_tag_environment
|
||||
ON release.release_tags (tenant_id, tag, COALESCE(environment_id, '00000000-0000-0000-0000-000000000000'::UUID));
|
||||
CREATE INDEX idx_release_tags_release ON release.release_tags(release_id);
|
||||
CREATE INDEX idx_release_tags_environment ON release.release_tags(environment_id)
|
||||
WHERE environment_id IS NOT NULL;
|
||||
|
||||
@@ -85,13 +85,14 @@ COMMENT ON COLUMN release.agent_capabilities.version IS 'Version of the capabili
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS release.agent_heartbeats (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
id UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
agent_id UUID NOT NULL,
|
||||
received_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
status JSONB NOT NULL,
|
||||
latency_ms INT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
PRIMARY KEY (id, created_at)
|
||||
-- No FK to agents for partition performance
|
||||
) PARTITION BY RANGE (created_at);
|
||||
|
||||
|
||||
@@ -87,10 +87,11 @@ CREATE TABLE IF NOT EXISTS release.plugins (
|
||||
entry_point TEXT,
|
||||
config_defaults JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (COALESCE(tenant_id, '00000000-0000-0000-0000-000000000000'::UUID), name)
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_plugins_scope_name
|
||||
ON release.plugins (COALESCE(tenant_id, '00000000-0000-0000-0000-000000000000'::UUID), name);
|
||||
CREATE INDEX idx_plugins_tenant ON release.plugins(tenant_id);
|
||||
CREATE INDEX idx_plugins_type ON release.plugins(plugin_type_id);
|
||||
CREATE INDEX idx_plugins_enabled ON release.plugins(tenant_id, is_enabled)
|
||||
@@ -154,10 +155,11 @@ CREATE TABLE IF NOT EXISTS release.plugin_instances (
|
||||
invocation_count BIGINT NOT NULL DEFAULT 0,
|
||||
error_count BIGINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
UNIQUE (tenant_id, plugin_id, COALESCE(instance_name, ''))
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_plugin_instances_scope_name
|
||||
ON release.plugin_instances (tenant_id, plugin_id, COALESCE(instance_name, ''));
|
||||
CREATE INDEX idx_plugin_instances_tenant ON release.plugin_instances(tenant_id);
|
||||
CREATE INDEX idx_plugin_instances_plugin ON release.plugin_instances(plugin_id);
|
||||
CREATE INDEX idx_plugin_instances_enabled ON release.plugin_instances(tenant_id, is_enabled)
|
||||
|
||||
@@ -28,11 +28,11 @@ CREATE TABLE IF NOT EXISTS release.policy_profiles (
|
||||
on_fail_hard TEXT[] NOT NULL DEFAULT '{}',
|
||||
created_by UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
-- Ensure unique names within tenant scope (NULL tenant = instance level)
|
||||
UNIQUE (COALESCE(tenant_id, '00000000-0000-0000-0000-000000000000'::UUID), name)
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_policy_profiles_scope_name
|
||||
ON release.policy_profiles (COALESCE(tenant_id, '00000000-0000-0000-0000-000000000000'::UUID), name);
|
||||
CREATE INDEX idx_policy_profiles_tenant ON release.policy_profiles(tenant_id);
|
||||
CREATE INDEX idx_policy_profiles_type ON release.policy_profiles(profile_type);
|
||||
CREATE INDEX idx_policy_profiles_default ON release.policy_profiles(tenant_id)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Npgsql;
|
||||
using StellaOps.Platform.Database.EfCore.CompiledModels;
|
||||
using StellaOps.Platform.Database.EfCore.Context;
|
||||
|
||||
namespace StellaOps.Platform.Database.Postgres;
|
||||
|
||||
public static class PlatformDbContextFactory
|
||||
{
|
||||
public const string DefaultSchemaName = "platform";
|
||||
|
||||
public static PlatformDbContext Create(NpgsqlConnection connection, int commandTimeoutSeconds, string schemaName)
|
||||
{
|
||||
var normalizedSchema = string.IsNullOrWhiteSpace(schemaName)
|
||||
? DefaultSchemaName
|
||||
: schemaName.Trim();
|
||||
|
||||
var optionsBuilder = new DbContextOptionsBuilder<PlatformDbContext>()
|
||||
.UseNpgsql(connection, npgsql => npgsql.CommandTimeout(commandTimeoutSeconds));
|
||||
|
||||
if (string.Equals(normalizedSchema, DefaultSchemaName, StringComparison.Ordinal))
|
||||
{
|
||||
// Use the static compiled model when schema mapping matches the default model.
|
||||
optionsBuilder.UseModel(PlatformDbContextModel.Instance);
|
||||
}
|
||||
|
||||
return new PlatformDbContext(optionsBuilder.Options, normalizedSchema);
|
||||
}
|
||||
}
|
||||
@@ -11,16 +11,38 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\AdvisoryAI\StellaOps.AdvisoryAI\StellaOps.AdvisoryAI.csproj" />
|
||||
<ProjectReference Include="..\..\..\AirGap\__Libraries\StellaOps.AirGap.Persistence\StellaOps.AirGap.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\Attestor\__Libraries\StellaOps.Attestor.Persistence\StellaOps.Attestor.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\BinaryIndex\__Libraries\StellaOps.BinaryIndex.Persistence\StellaOps.BinaryIndex.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\BinaryIndex\__Libraries\StellaOps.BinaryIndex.GoldenSet\StellaOps.BinaryIndex.GoldenSet.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Artifact.Infrastructure\StellaOps.Artifact.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\..\Authority\__Libraries\StellaOps.Authority.Persistence\StellaOps.Authority.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\Concelier\__Libraries\StellaOps.Concelier.Persistence\StellaOps.Concelier.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\Graph\__Libraries\StellaOps.Graph.Indexer.Persistence\StellaOps.Graph.Indexer.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Evidence.Persistence\StellaOps.Evidence.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\Excititor\__Libraries\StellaOps.Excititor.Persistence\StellaOps.Excititor.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\Notify\__Libraries\StellaOps.Notify.Persistence\StellaOps.Notify.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\Plugin\StellaOps.Plugin.Registry\StellaOps.Plugin.Registry.csproj" />
|
||||
<ProjectReference Include="..\..\..\Policy\__Libraries\StellaOps.Policy.Persistence\StellaOps.Policy.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\SbomService\__Libraries\StellaOps.SbomService.Lineage\StellaOps.SbomService.Lineage.csproj" />
|
||||
<ProjectReference Include="..\..\..\Scanner\__Libraries\StellaOps.Scanner.Storage\StellaOps.Scanner.Storage.csproj" />
|
||||
<ProjectReference Include="..\..\..\Signals\__Libraries\StellaOps.Signals.Persistence\StellaOps.Signals.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\Scanner\__Libraries\StellaOps.Scanner.Triage\StellaOps.Scanner.Triage.csproj" />
|
||||
<ProjectReference Include="..\..\..\Scheduler\__Libraries\StellaOps.Scheduler.Persistence\StellaOps.Scheduler.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\Timeline\__Libraries\StellaOps.Timeline.Core\StellaOps.Timeline.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\TimelineIndexer\StellaOps.TimelineIndexer\StellaOps.TimelineIndexer.Infrastructure\StellaOps.TimelineIndexer.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\..\Unknowns\__Libraries\StellaOps.Unknowns.Persistence\StellaOps.Unknowns.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\VexHub\__Libraries\StellaOps.VexHub.Persistence\StellaOps.VexHub.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\VexLens\StellaOps.VexLens.Persistence\StellaOps.VexLens.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\Remediation\StellaOps.Remediation.Persistence\StellaOps.Remediation.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.ReachGraph.Persistence\StellaOps.ReachGraph.Persistence.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Verdict\StellaOps.Verdict.csproj" />
|
||||
<ProjectReference Include="..\..\..\EvidenceLocker\StellaOps.EvidenceLocker\StellaOps.EvidenceLocker.Infrastructure\StellaOps.EvidenceLocker.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Eventing\StellaOps.Eventing.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Infrastructure.Postgres\StellaOps.Infrastructure.Postgres.csproj" />
|
||||
<ProjectReference Include="..\..\..\Findings\StellaOps.Findings.Ledger\StellaOps.Findings.Ledger.csproj" />
|
||||
<ProjectReference Include="..\..\..\Orchestrator\StellaOps.Orchestrator\StellaOps.Orchestrator.Infrastructure\StellaOps.Orchestrator.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -28,6 +50,17 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Prevent automatic compiled-model binding so non-default schemas can build runtime models. -->
|
||||
<Compile Remove="EfCore\CompiledModels\PlatformDbContextAssemblyAttributes.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" PrivateAssets="all" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Migrations\**\*.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -6,6 +6,7 @@ Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_sol
|
||||
| --- | --- | --- |
|
||||
| SPRINT_20260222_051-MGC-04-W1 | DONE | Added platform-owned `MigrationModuleRegistry` canonical module catalog for migration runner entrypoint consolidation; CLI now consumes this registry instead of owning module metadata. |
|
||||
| SPRINT_20260222_051-MGC-04-W1-PLUGINS | DONE | Replaced hardcoded module catalog with auto-discovered migration plugins (`IMigrationModulePlugin`) so one consolidated plugin descriptor per web service feeds both CLI and Platform API migration execution paths. |
|
||||
| SPRINT_20260222_051-MGC-04-W1-SOURCES | DONE | Extended service plugin model to support source-set flattening (multiple migration sources per web service), including Scanner storage+triage source registration under one `ScannerMigrationModulePlugin`, plus synthesized per-plugin consolidated migration artifact generation for empty-history execution and partial-history backfill self-healing. |
|
||||
| B22-01-DB | DONE | Sprint `docs/implplan/SPRINT_20260220_018_Platform_pack22_backend_contracts_and_migrations.md`: added release migration `047_GlobalContextAndFilters.sql` with `platform.context_regions`, `platform.context_environments`, and `platform.ui_context_preferences`. |
|
||||
| B22-02-DB | DONE | Sprint `docs/implplan/SPRINT_20260220_018_Platform_pack22_backend_contracts_and_migrations.md`: added release migration `048_ReleaseReadModels.sql` with release list/activity/approvals projection tables, correlation keys, and deterministic ordering indexes. |
|
||||
| B22-03-DB | DONE | Sprint `docs/implplan/SPRINT_20260220_018_Platform_pack22_backend_contracts_and_migrations.md`: added release migration `049_TopologyInventory.sql` with normalized topology inventory projection tables and sync-watermark indexes. |
|
||||
@@ -13,3 +14,8 @@ Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_sol
|
||||
| B22-05-DB | DONE | Sprint `docs/implplan/SPRINT_20260220_018_Platform_pack22_backend_contracts_and_migrations.md`: added release migration `051_IntegrationSourceHealth.sql` for integrations feed and VEX source health/freshness read-model projection objects. |
|
||||
| REMED-05 | TODO | Remediation checklist: docs/implplan/audits/csproj-standards/remediation/checklists/src/Platform/__Libraries/StellaOps.Platform.Database/StellaOps.Platform.Database.md. |
|
||||
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
|
||||
| PLATFORM-EF-01 | DONE | Sprint `docs/implplan/SPRINT_20260222_096_Platform_dal_to_efcore.md`: verified AGENTS.md alignment and `PlatformMigrationModulePlugin` registration in migration registry. |
|
||||
| PLATFORM-EF-02 | DONE | Sprint 096: scaffolded EF Core model baseline under `EfCore/Context/`, `EfCore/Models/`, `EfCore/CompiledModels/` for platform schema tables (`environment_settings`, `context_regions`, `context_environments`, `ui_context_preferences`). |
|
||||
| PLATFORM-EF-03 | DONE | Sprint 096: converted `PostgresEnvironmentSettingsStore` and `PostgresPlatformContextStore` reads to EF Core LINQ with `AsNoTracking()`; PostgreSQL-specific upserts retained as raw SQL. `PostgresScoreHistoryStore` retained as raw Npgsql (cross-module signals schema). |
|
||||
| PLATFORM-EF-04 | DONE | Sprint 096: added design-time factory (`PlatformDesignTimeDbContextFactory`), runtime factory (`PlatformDbContextFactory`) with `UseModel(PlatformDbContextModel.Instance)` for default schema, compiled model stubs with `// <auto-generated />` header, assembly attribute exclusion in csproj. |
|
||||
| PLATFORM-EF-05 | DONE | Sprint 096: sequential builds pass for Platform.Database (0W/0E), Platform.WebService (0W/0E), Platform.WebService.Tests (0W/0E). TASKS.md and sprint tracker updated. |
|
||||
|
||||
Reference in New Issue
Block a user