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,9 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using StellaOps.AirGap.Persistence.EfCore.CompiledModels;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Context;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
[assembly: DbContextModel(typeof(AirGapDbContext), typeof(AirGapDbContextModel))]
|
||||
@@ -0,0 +1,48 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Context;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.EfCore.CompiledModels
|
||||
{
|
||||
[DbContext(typeof(AirGapDbContext))]
|
||||
public partial class AirGapDbContextModel : RuntimeModel
|
||||
{
|
||||
private static readonly bool _useOldBehavior31751 =
|
||||
System.AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31751", out var enabled31751) && enabled31751;
|
||||
|
||||
static AirGapDbContextModel()
|
||||
{
|
||||
var model = new AirGapDbContextModel();
|
||||
|
||||
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 = (AirGapDbContextModel)model.FinalizeModel();
|
||||
}
|
||||
|
||||
private static AirGapDbContextModel _instance;
|
||||
public static IModel Instance => _instance;
|
||||
|
||||
partial void Initialize();
|
||||
|
||||
partial void Customize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// <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.AirGap.Persistence.EfCore.CompiledModels
|
||||
{
|
||||
public partial class AirGapDbContextModel
|
||||
{
|
||||
private AirGapDbContextModel()
|
||||
: base(skipDetectChanges: false, modelId: new Guid("fd07ee8a-66dd-4965-a96c-9898cb1ec690"), entityTypeCount: 3)
|
||||
{
|
||||
}
|
||||
|
||||
partial void Initialize()
|
||||
{
|
||||
var bundleVersion = BundleVersionEntityType.Create(this);
|
||||
var bundleVersionHistory = BundleVersionHistoryEntityType.Create(this);
|
||||
var state = StateEntityType.Create(this);
|
||||
|
||||
BundleVersionEntityType.CreateAnnotations(bundleVersion);
|
||||
BundleVersionHistoryEntityType.CreateAnnotations(bundleVersionHistory);
|
||||
StateEntityType.CreateAnnotations(state);
|
||||
|
||||
AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
AddAnnotation("ProductVersion", "10.0.0");
|
||||
AddAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.EfCore.CompiledModels
|
||||
{
|
||||
[EntityFrameworkInternal]
|
||||
public partial class BundleVersionEntityType
|
||||
{
|
||||
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
|
||||
{
|
||||
var runtimeEntityType = model.AddEntityType(
|
||||
"StellaOps.AirGap.Persistence.EfCore.Models.BundleVersion",
|
||||
typeof(BundleVersion),
|
||||
baseEntityType,
|
||||
propertyCount: 14,
|
||||
namedIndexCount: 1,
|
||||
keyCount: 1);
|
||||
|
||||
var tenantId = runtimeEntityType.AddProperty(
|
||||
"TenantId",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("TenantId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<TenantId>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
afterSaveBehavior: PropertySaveBehavior.Throw);
|
||||
tenantId.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
tenantId.AddAnnotation("Relational:ColumnName", "tenant_id");
|
||||
|
||||
var bundleType = runtimeEntityType.AddProperty(
|
||||
"BundleType",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("BundleType", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<BundleType>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
afterSaveBehavior: PropertySaveBehavior.Throw);
|
||||
bundleType.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
bundleType.AddAnnotation("Relational:ColumnName", "bundle_type");
|
||||
|
||||
var activatedAt = runtimeEntityType.AddProperty(
|
||||
"ActivatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("ActivatedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<ActivatedAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
activatedAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
activatedAt.AddAnnotation("Relational:ColumnName", "activated_at");
|
||||
|
||||
var bundleCreatedAt = runtimeEntityType.AddProperty(
|
||||
"BundleCreatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("BundleCreatedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<BundleCreatedAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
bundleCreatedAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
bundleCreatedAt.AddAnnotation("Relational:ColumnName", "bundle_created_at");
|
||||
|
||||
var bundleDigest = runtimeEntityType.AddProperty(
|
||||
"BundleDigest",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("BundleDigest", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<BundleDigest>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly));
|
||||
bundleDigest.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
bundleDigest.AddAnnotation("Relational:ColumnName", "bundle_digest");
|
||||
|
||||
var createdAt = runtimeEntityType.AddProperty(
|
||||
"CreatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("CreatedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<CreatedAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
valueGenerated: ValueGenerated.OnAdd,
|
||||
sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
createdAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
createdAt.AddAnnotation("Relational:ColumnName", "created_at");
|
||||
createdAt.AddAnnotation("Relational:DefaultValueSql", "now()");
|
||||
|
||||
var forceActivateReason = runtimeEntityType.AddProperty(
|
||||
"ForceActivateReason",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("ForceActivateReason", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<ForceActivateReason>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
nullable: true);
|
||||
forceActivateReason.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
forceActivateReason.AddAnnotation("Relational:ColumnName", "force_activate_reason");
|
||||
|
||||
var major = runtimeEntityType.AddProperty(
|
||||
"Major",
|
||||
typeof(int),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("Major", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<Major>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: 0);
|
||||
major.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
major.AddAnnotation("Relational:ColumnName", "major");
|
||||
|
||||
var minor = runtimeEntityType.AddProperty(
|
||||
"Minor",
|
||||
typeof(int),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("Minor", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<Minor>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: 0);
|
||||
minor.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
minor.AddAnnotation("Relational:ColumnName", "minor");
|
||||
|
||||
var patch = runtimeEntityType.AddProperty(
|
||||
"Patch",
|
||||
typeof(int),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("Patch", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<Patch>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: 0);
|
||||
patch.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
patch.AddAnnotation("Relational:ColumnName", "patch");
|
||||
|
||||
var prerelease = runtimeEntityType.AddProperty(
|
||||
"Prerelease",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("Prerelease", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<Prerelease>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
nullable: true);
|
||||
prerelease.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
prerelease.AddAnnotation("Relational:ColumnName", "prerelease");
|
||||
|
||||
var updatedAt = runtimeEntityType.AddProperty(
|
||||
"UpdatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("UpdatedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<UpdatedAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
valueGenerated: ValueGenerated.OnAdd,
|
||||
sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
updatedAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
updatedAt.AddAnnotation("Relational:ColumnName", "updated_at");
|
||||
updatedAt.AddAnnotation("Relational:DefaultValueSql", "now()");
|
||||
|
||||
var versionString = runtimeEntityType.AddProperty(
|
||||
"VersionString",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("VersionString", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<VersionString>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly));
|
||||
versionString.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
versionString.AddAnnotation("Relational:ColumnName", "version_string");
|
||||
|
||||
var wasForceActivated = runtimeEntityType.AddProperty(
|
||||
"WasForceActivated",
|
||||
typeof(bool),
|
||||
propertyInfo: typeof(BundleVersion).GetProperty("WasForceActivated", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersion).GetField("<WasForceActivated>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: false);
|
||||
wasForceActivated.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
wasForceActivated.AddAnnotation("Relational:ColumnName", "was_force_activated");
|
||||
|
||||
var key = runtimeEntityType.AddKey(
|
||||
new[] { tenantId, bundleType });
|
||||
runtimeEntityType.SetPrimaryKey(key);
|
||||
key.AddAnnotation("Relational:Name", "bundle_versions_pkey");
|
||||
|
||||
var idx_airgap_bundle_versions_tenant = runtimeEntityType.AddIndex(
|
||||
new[] { tenantId },
|
||||
name: "idx_airgap_bundle_versions_tenant");
|
||||
|
||||
return runtimeEntityType;
|
||||
}
|
||||
|
||||
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
|
||||
{
|
||||
runtimeEntityType.AddAnnotation("Relational:FunctionName", null);
|
||||
runtimeEntityType.AddAnnotation("Relational:Schema", "airgap");
|
||||
runtimeEntityType.AddAnnotation("Relational:SqlQuery", null);
|
||||
runtimeEntityType.AddAnnotation("Relational:TableName", "bundle_versions");
|
||||
runtimeEntityType.AddAnnotation("Relational:ViewName", null);
|
||||
runtimeEntityType.AddAnnotation("Relational:ViewSchema", null);
|
||||
|
||||
Customize(runtimeEntityType);
|
||||
}
|
||||
|
||||
static partial void Customize(RuntimeEntityType runtimeEntityType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.EfCore.CompiledModels
|
||||
{
|
||||
[EntityFrameworkInternal]
|
||||
public partial class BundleVersionHistoryEntityType
|
||||
{
|
||||
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
|
||||
{
|
||||
var runtimeEntityType = model.AddEntityType(
|
||||
"StellaOps.AirGap.Persistence.EfCore.Models.BundleVersionHistory",
|
||||
typeof(BundleVersionHistory),
|
||||
baseEntityType,
|
||||
propertyCount: 15,
|
||||
namedIndexCount: 1,
|
||||
keyCount: 1);
|
||||
|
||||
var id = runtimeEntityType.AddProperty(
|
||||
"Id",
|
||||
typeof(long),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<Id>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
valueGenerated: ValueGenerated.OnAdd,
|
||||
afterSaveBehavior: PropertySaveBehavior.Throw,
|
||||
sentinel: 0L);
|
||||
id.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
id.AddAnnotation("Relational:ColumnName", "id");
|
||||
id.AddAnnotation("Relational:DefaultValueSql", "nextval('bundle_version_history_id_seq'::regclass)");
|
||||
|
||||
var activatedAt = runtimeEntityType.AddProperty(
|
||||
"ActivatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("ActivatedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<ActivatedAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
activatedAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
activatedAt.AddAnnotation("Relational:ColumnName", "activated_at");
|
||||
|
||||
var bundleCreatedAt = runtimeEntityType.AddProperty(
|
||||
"BundleCreatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("BundleCreatedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<BundleCreatedAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
bundleCreatedAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
bundleCreatedAt.AddAnnotation("Relational:ColumnName", "bundle_created_at");
|
||||
|
||||
var bundleDigest = runtimeEntityType.AddProperty(
|
||||
"BundleDigest",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("BundleDigest", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<BundleDigest>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly));
|
||||
bundleDigest.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
bundleDigest.AddAnnotation("Relational:ColumnName", "bundle_digest");
|
||||
|
||||
var bundleType = runtimeEntityType.AddProperty(
|
||||
"BundleType",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("BundleType", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<BundleType>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly));
|
||||
bundleType.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
bundleType.AddAnnotation("Relational:ColumnName", "bundle_type");
|
||||
|
||||
var createdAt = runtimeEntityType.AddProperty(
|
||||
"CreatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("CreatedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<CreatedAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
valueGenerated: ValueGenerated.OnAdd,
|
||||
sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
createdAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
createdAt.AddAnnotation("Relational:ColumnName", "created_at");
|
||||
createdAt.AddAnnotation("Relational:DefaultValueSql", "now()");
|
||||
|
||||
var deactivatedAt = runtimeEntityType.AddProperty(
|
||||
"DeactivatedAt",
|
||||
typeof(DateTime?),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("DeactivatedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<DeactivatedAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
nullable: true);
|
||||
deactivatedAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
deactivatedAt.AddAnnotation("Relational:ColumnName", "deactivated_at");
|
||||
|
||||
var forceActivateReason = runtimeEntityType.AddProperty(
|
||||
"ForceActivateReason",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("ForceActivateReason", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<ForceActivateReason>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
nullable: true);
|
||||
forceActivateReason.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
forceActivateReason.AddAnnotation("Relational:ColumnName", "force_activate_reason");
|
||||
|
||||
var major = runtimeEntityType.AddProperty(
|
||||
"Major",
|
||||
typeof(int),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("Major", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<Major>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: 0);
|
||||
major.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
major.AddAnnotation("Relational:ColumnName", "major");
|
||||
|
||||
var minor = runtimeEntityType.AddProperty(
|
||||
"Minor",
|
||||
typeof(int),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("Minor", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<Minor>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: 0);
|
||||
minor.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
minor.AddAnnotation("Relational:ColumnName", "minor");
|
||||
|
||||
var patch = runtimeEntityType.AddProperty(
|
||||
"Patch",
|
||||
typeof(int),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("Patch", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<Patch>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: 0);
|
||||
patch.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
patch.AddAnnotation("Relational:ColumnName", "patch");
|
||||
|
||||
var prerelease = runtimeEntityType.AddProperty(
|
||||
"Prerelease",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("Prerelease", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<Prerelease>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
nullable: true);
|
||||
prerelease.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
prerelease.AddAnnotation("Relational:ColumnName", "prerelease");
|
||||
|
||||
var tenantId = runtimeEntityType.AddProperty(
|
||||
"TenantId",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("TenantId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<TenantId>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly));
|
||||
tenantId.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
tenantId.AddAnnotation("Relational:ColumnName", "tenant_id");
|
||||
|
||||
var versionString = runtimeEntityType.AddProperty(
|
||||
"VersionString",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("VersionString", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<VersionString>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly));
|
||||
versionString.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
versionString.AddAnnotation("Relational:ColumnName", "version_string");
|
||||
|
||||
var wasForceActivated = runtimeEntityType.AddProperty(
|
||||
"WasForceActivated",
|
||||
typeof(bool),
|
||||
propertyInfo: typeof(BundleVersionHistory).GetProperty("WasForceActivated", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(BundleVersionHistory).GetField("<WasForceActivated>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: false);
|
||||
wasForceActivated.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
wasForceActivated.AddAnnotation("Relational:ColumnName", "was_force_activated");
|
||||
|
||||
var key = runtimeEntityType.AddKey(
|
||||
new[] { id });
|
||||
runtimeEntityType.SetPrimaryKey(key);
|
||||
key.AddAnnotation("Relational:Name", "bundle_version_history_pkey");
|
||||
|
||||
var idx_airgap_bundle_version_history_tenant = runtimeEntityType.AddIndex(
|
||||
new[] { tenantId, bundleType, activatedAt },
|
||||
name: "idx_airgap_bundle_version_history_tenant");
|
||||
|
||||
return runtimeEntityType;
|
||||
}
|
||||
|
||||
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
|
||||
{
|
||||
runtimeEntityType.AddAnnotation("Relational:FunctionName", null);
|
||||
runtimeEntityType.AddAnnotation("Relational:Schema", "airgap");
|
||||
runtimeEntityType.AddAnnotation("Relational:SqlQuery", null);
|
||||
runtimeEntityType.AddAnnotation("Relational:TableName", "bundle_version_history");
|
||||
runtimeEntityType.AddAnnotation("Relational:ViewName", null);
|
||||
runtimeEntityType.AddAnnotation("Relational:ViewSchema", null);
|
||||
|
||||
Customize(runtimeEntityType);
|
||||
}
|
||||
|
||||
static partial void Customize(RuntimeEntityType runtimeEntityType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
|
||||
#pragma warning disable 219, 612, 618
|
||||
#nullable disable
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.EfCore.CompiledModels
|
||||
{
|
||||
[EntityFrameworkInternal]
|
||||
public partial class StateEntityType
|
||||
{
|
||||
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
|
||||
{
|
||||
var runtimeEntityType = model.AddEntityType(
|
||||
"StellaOps.AirGap.Persistence.EfCore.Models.State",
|
||||
typeof(State),
|
||||
baseEntityType,
|
||||
propertyCount: 11,
|
||||
namedIndexCount: 2,
|
||||
keyCount: 1);
|
||||
|
||||
var tenantId = runtimeEntityType.AddProperty(
|
||||
"TenantId",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(State).GetProperty("TenantId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<TenantId>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
afterSaveBehavior: PropertySaveBehavior.Throw);
|
||||
tenantId.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
tenantId.AddAnnotation("Relational:ColumnName", "tenant_id");
|
||||
|
||||
var contentBudgets = runtimeEntityType.AddProperty(
|
||||
"ContentBudgets",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(State).GetProperty("ContentBudgets", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<ContentBudgets>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
valueGenerated: ValueGenerated.OnAdd);
|
||||
contentBudgets.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
contentBudgets.AddAnnotation("Relational:ColumnName", "content_budgets");
|
||||
contentBudgets.AddAnnotation("Relational:ColumnType", "jsonb");
|
||||
contentBudgets.AddAnnotation("Relational:DefaultValueSql", "'{}'::jsonb");
|
||||
|
||||
var createdAt = runtimeEntityType.AddProperty(
|
||||
"CreatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(State).GetProperty("CreatedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<CreatedAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
valueGenerated: ValueGenerated.OnAdd,
|
||||
sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
createdAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
createdAt.AddAnnotation("Relational:ColumnName", "created_at");
|
||||
createdAt.AddAnnotation("Relational:DefaultValueSql", "now()");
|
||||
|
||||
var driftBaselineSeconds = runtimeEntityType.AddProperty(
|
||||
"DriftBaselineSeconds",
|
||||
typeof(long),
|
||||
propertyInfo: typeof(State).GetProperty("DriftBaselineSeconds", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<DriftBaselineSeconds>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: 0L);
|
||||
driftBaselineSeconds.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
driftBaselineSeconds.AddAnnotation("Relational:ColumnName", "drift_baseline_seconds");
|
||||
|
||||
var id = runtimeEntityType.AddProperty(
|
||||
"Id",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(State).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<Id>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
nullable: true);
|
||||
id.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
id.AddAnnotation("Relational:ColumnName", "id");
|
||||
|
||||
var lastTransitionAt = runtimeEntityType.AddProperty(
|
||||
"LastTransitionAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(State).GetProperty("LastTransitionAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<LastTransitionAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
valueGenerated: ValueGenerated.OnAdd,
|
||||
sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
lastTransitionAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
lastTransitionAt.AddAnnotation("Relational:ColumnName", "last_transition_at");
|
||||
lastTransitionAt.AddAnnotation("Relational:DefaultValueSql", "'0001-01-01 00:00:00+00'::timestamp with time zone");
|
||||
|
||||
var policyHash = runtimeEntityType.AddProperty(
|
||||
"PolicyHash",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(State).GetProperty("PolicyHash", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<PolicyHash>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
nullable: true);
|
||||
policyHash.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
policyHash.AddAnnotation("Relational:ColumnName", "policy_hash");
|
||||
|
||||
var @sealed = runtimeEntityType.AddProperty(
|
||||
"Sealed",
|
||||
typeof(bool),
|
||||
propertyInfo: typeof(State).GetProperty("Sealed", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<Sealed>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
sentinel: false);
|
||||
@sealed.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
@sealed.AddAnnotation("Relational:ColumnName", "sealed");
|
||||
|
||||
var stalenessBudget = runtimeEntityType.AddProperty(
|
||||
"StalenessBudget",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(State).GetProperty("StalenessBudget", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<StalenessBudget>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
valueGenerated: ValueGenerated.OnAdd);
|
||||
stalenessBudget.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
stalenessBudget.AddAnnotation("Relational:ColumnName", "staleness_budget");
|
||||
stalenessBudget.AddAnnotation("Relational:ColumnType", "jsonb");
|
||||
stalenessBudget.AddAnnotation("Relational:DefaultValueSql", "'{\"breachSeconds\": 7200, \"warningSeconds\": 3600}'::jsonb");
|
||||
|
||||
var timeAnchor = runtimeEntityType.AddProperty(
|
||||
"TimeAnchor",
|
||||
typeof(string),
|
||||
propertyInfo: typeof(State).GetProperty("TimeAnchor", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<TimeAnchor>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
valueGenerated: ValueGenerated.OnAdd);
|
||||
timeAnchor.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
timeAnchor.AddAnnotation("Relational:ColumnName", "time_anchor");
|
||||
timeAnchor.AddAnnotation("Relational:ColumnType", "jsonb");
|
||||
timeAnchor.AddAnnotation("Relational:DefaultValueSql", "'{}'::jsonb");
|
||||
|
||||
var updatedAt = runtimeEntityType.AddProperty(
|
||||
"UpdatedAt",
|
||||
typeof(DateTime),
|
||||
propertyInfo: typeof(State).GetProperty("UpdatedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
fieldInfo: typeof(State).GetField("<UpdatedAt>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
||||
valueGenerated: ValueGenerated.OnAdd,
|
||||
sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
updatedAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None);
|
||||
updatedAt.AddAnnotation("Relational:ColumnName", "updated_at");
|
||||
updatedAt.AddAnnotation("Relational:DefaultValueSql", "now()");
|
||||
|
||||
var key = runtimeEntityType.AddKey(
|
||||
new[] { tenantId });
|
||||
runtimeEntityType.SetPrimaryKey(key);
|
||||
key.AddAnnotation("Relational:Name", "state_pkey");
|
||||
|
||||
var idx_airgap_state_sealed = runtimeEntityType.AddIndex(
|
||||
new[] { @sealed },
|
||||
name: "idx_airgap_state_sealed");
|
||||
idx_airgap_state_sealed.AddAnnotation("Relational:Filter", "(sealed = true)");
|
||||
|
||||
var idx_airgap_state_tenant = runtimeEntityType.AddIndex(
|
||||
new[] { tenantId },
|
||||
name: "idx_airgap_state_tenant");
|
||||
|
||||
return runtimeEntityType;
|
||||
}
|
||||
|
||||
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
|
||||
{
|
||||
runtimeEntityType.AddAnnotation("Relational:FunctionName", null);
|
||||
runtimeEntityType.AddAnnotation("Relational:Schema", "airgap");
|
||||
runtimeEntityType.AddAnnotation("Relational:SqlQuery", null);
|
||||
runtimeEntityType.AddAnnotation("Relational:TableName", "state");
|
||||
runtimeEntityType.AddAnnotation("Relational:ViewName", null);
|
||||
runtimeEntityType.AddAnnotation("Relational:ViewSchema", null);
|
||||
|
||||
Customize(runtimeEntityType);
|
||||
}
|
||||
|
||||
static partial void Customize(RuntimeEntityType runtimeEntityType);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.AirGap.Persistence.Postgres;
|
||||
using StellaOps.Infrastructure.Postgres.Options;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.EfCore.Context;
|
||||
|
||||
/// <summary>
|
||||
/// EF Core DbContext for AirGap module.
|
||||
/// This is a stub that will be scaffolded from the PostgreSQL database.
|
||||
/// </summary>
|
||||
public class AirGapDbContext : DbContext
|
||||
public partial class AirGapDbContext : DbContext
|
||||
{
|
||||
private readonly string _schemaName;
|
||||
|
||||
public AirGapDbContext(DbContextOptions<AirGapDbContext> options)
|
||||
: this(options, null)
|
||||
{
|
||||
}
|
||||
|
||||
public AirGapDbContext(DbContextOptions<AirGapDbContext> options, IOptions<PostgresOptions>? postgresOptions)
|
||||
public AirGapDbContext(DbContextOptions<AirGapDbContext> options, string? schemaName = null)
|
||||
: base(options)
|
||||
{
|
||||
var schema = postgresOptions?.Value.SchemaName;
|
||||
_schemaName = string.IsNullOrWhiteSpace(schema)
|
||||
? AirGapDataSource.DefaultSchemaName
|
||||
: schema;
|
||||
_schemaName = string.IsNullOrWhiteSpace(schemaName)
|
||||
? "airgap"
|
||||
: schemaName.Trim();
|
||||
}
|
||||
|
||||
public virtual DbSet<BundleVersion> BundleVersions { get; set; }
|
||||
|
||||
public virtual DbSet<BundleVersionHistory> BundleVersionHistories { get; set; }
|
||||
|
||||
public virtual DbSet<State> States { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.HasDefaultSchema(_schemaName);
|
||||
base.OnModelCreating(modelBuilder);
|
||||
var schemaName = _schemaName;
|
||||
|
||||
modelBuilder.Entity<BundleVersion>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.TenantId, e.BundleType }).HasName("bundle_versions_pkey");
|
||||
|
||||
entity.ToTable("bundle_versions", schemaName);
|
||||
|
||||
entity.HasIndex(e => e.TenantId, "idx_airgap_bundle_versions_tenant");
|
||||
|
||||
entity.Property(e => e.TenantId).HasColumnName("tenant_id");
|
||||
entity.Property(e => e.BundleType).HasColumnName("bundle_type");
|
||||
entity.Property(e => e.ActivatedAt).HasColumnName("activated_at");
|
||||
entity.Property(e => e.BundleCreatedAt).HasColumnName("bundle_created_at");
|
||||
entity.Property(e => e.BundleDigest).HasColumnName("bundle_digest");
|
||||
entity.Property(e => e.CreatedAt)
|
||||
.HasDefaultValueSql("now()")
|
||||
.HasColumnName("created_at");
|
||||
entity.Property(e => e.ForceActivateReason).HasColumnName("force_activate_reason");
|
||||
entity.Property(e => e.Major).HasColumnName("major");
|
||||
entity.Property(e => e.Minor).HasColumnName("minor");
|
||||
entity.Property(e => e.Patch).HasColumnName("patch");
|
||||
entity.Property(e => e.Prerelease).HasColumnName("prerelease");
|
||||
entity.Property(e => e.UpdatedAt)
|
||||
.HasDefaultValueSql("now()")
|
||||
.HasColumnName("updated_at");
|
||||
entity.Property(e => e.VersionString).HasColumnName("version_string");
|
||||
entity.Property(e => e.WasForceActivated).HasColumnName("was_force_activated");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<BundleVersionHistory>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("bundle_version_history_pkey");
|
||||
|
||||
entity.ToTable("bundle_version_history", schemaName);
|
||||
|
||||
entity.HasIndex(e => new { e.TenantId, e.BundleType, e.ActivatedAt }, "idx_airgap_bundle_version_history_tenant").IsDescending(false, false, true);
|
||||
|
||||
entity.Property(e => e.Id)
|
||||
.HasDefaultValueSql("nextval('bundle_version_history_id_seq'::regclass)")
|
||||
.HasColumnName("id");
|
||||
entity.Property(e => e.ActivatedAt).HasColumnName("activated_at");
|
||||
entity.Property(e => e.BundleCreatedAt).HasColumnName("bundle_created_at");
|
||||
entity.Property(e => e.BundleDigest).HasColumnName("bundle_digest");
|
||||
entity.Property(e => e.BundleType).HasColumnName("bundle_type");
|
||||
entity.Property(e => e.CreatedAt)
|
||||
.HasDefaultValueSql("now()")
|
||||
.HasColumnName("created_at");
|
||||
entity.Property(e => e.DeactivatedAt).HasColumnName("deactivated_at");
|
||||
entity.Property(e => e.ForceActivateReason).HasColumnName("force_activate_reason");
|
||||
entity.Property(e => e.Major).HasColumnName("major");
|
||||
entity.Property(e => e.Minor).HasColumnName("minor");
|
||||
entity.Property(e => e.Patch).HasColumnName("patch");
|
||||
entity.Property(e => e.Prerelease).HasColumnName("prerelease");
|
||||
entity.Property(e => e.TenantId).HasColumnName("tenant_id");
|
||||
entity.Property(e => e.VersionString).HasColumnName("version_string");
|
||||
entity.Property(e => e.WasForceActivated).HasColumnName("was_force_activated");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<State>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.TenantId).HasName("state_pkey");
|
||||
|
||||
entity.ToTable("state", schemaName);
|
||||
|
||||
entity.HasIndex(e => e.Sealed, "idx_airgap_state_sealed").HasFilter("(sealed = true)");
|
||||
|
||||
entity.HasIndex(e => e.TenantId, "idx_airgap_state_tenant");
|
||||
|
||||
entity.Property(e => e.TenantId).HasColumnName("tenant_id");
|
||||
entity.Property(e => e.ContentBudgets)
|
||||
.HasDefaultValueSql("'{}'::jsonb")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("content_budgets");
|
||||
entity.Property(e => e.CreatedAt)
|
||||
.HasDefaultValueSql("now()")
|
||||
.HasColumnName("created_at");
|
||||
entity.Property(e => e.DriftBaselineSeconds).HasColumnName("drift_baseline_seconds");
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.LastTransitionAt)
|
||||
.HasDefaultValueSql("'0001-01-01 00:00:00+00'::timestamp with time zone")
|
||||
.HasColumnName("last_transition_at");
|
||||
entity.Property(e => e.PolicyHash).HasColumnName("policy_hash");
|
||||
entity.Property(e => e.Sealed).HasColumnName("sealed");
|
||||
entity.Property(e => e.StalenessBudget)
|
||||
.HasDefaultValueSql("'{\"breachSeconds\": 7200, \"warningSeconds\": 3600}'::jsonb")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("staleness_budget");
|
||||
entity.Property(e => e.TimeAnchor)
|
||||
.HasDefaultValueSql("'{}'::jsonb")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("time_anchor");
|
||||
entity.Property(e => e.UpdatedAt)
|
||||
.HasDefaultValueSql("now()")
|
||||
.HasColumnName("updated_at");
|
||||
});
|
||||
|
||||
OnModelCreatingPartial(modelBuilder);
|
||||
}
|
||||
|
||||
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.EfCore.Context;
|
||||
|
||||
public sealed class AirGapDesignTimeDbContextFactory : IDesignTimeDbContextFactory<AirGapDbContext>
|
||||
{
|
||||
private const string DefaultConnectionString = "Host=localhost;Port=55434;Database=postgres;Username=postgres;Password=postgres;Search Path=airgap,public";
|
||||
private const string ConnectionStringEnvironmentVariable = "STELLAOPS_AIRGAP_EF_CONNECTION";
|
||||
|
||||
public AirGapDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var connectionString = ResolveConnectionString();
|
||||
var options = new DbContextOptionsBuilder<AirGapDbContext>()
|
||||
.UseNpgsql(connectionString)
|
||||
.Options;
|
||||
|
||||
return new AirGapDbContext(options);
|
||||
}
|
||||
|
||||
private static string ResolveConnectionString()
|
||||
{
|
||||
var fromEnvironment = Environment.GetEnvironmentVariable(ConnectionStringEnvironmentVariable);
|
||||
return string.IsNullOrWhiteSpace(fromEnvironment) ? DefaultConnectionString : fromEnvironment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
|
||||
public partial class BundleVersion
|
||||
{
|
||||
public string TenantId { get; set; } = null!;
|
||||
|
||||
public string BundleType { get; set; } = null!;
|
||||
|
||||
public string VersionString { get; set; } = null!;
|
||||
|
||||
public int Major { get; set; }
|
||||
|
||||
public int Minor { get; set; }
|
||||
|
||||
public int Patch { get; set; }
|
||||
|
||||
public string? Prerelease { get; set; }
|
||||
|
||||
public DateTime BundleCreatedAt { get; set; }
|
||||
|
||||
public string BundleDigest { get; set; } = null!;
|
||||
|
||||
public DateTime ActivatedAt { get; set; }
|
||||
|
||||
public bool WasForceActivated { get; set; }
|
||||
|
||||
public string? ForceActivateReason { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
|
||||
public partial class BundleVersionHistory
|
||||
{
|
||||
public long Id { get; set; }
|
||||
|
||||
public string TenantId { get; set; } = null!;
|
||||
|
||||
public string BundleType { get; set; } = null!;
|
||||
|
||||
public string VersionString { get; set; } = null!;
|
||||
|
||||
public int Major { get; set; }
|
||||
|
||||
public int Minor { get; set; }
|
||||
|
||||
public int Patch { get; set; }
|
||||
|
||||
public string? Prerelease { get; set; }
|
||||
|
||||
public DateTime BundleCreatedAt { get; set; }
|
||||
|
||||
public string BundleDigest { get; set; } = null!;
|
||||
|
||||
public DateTime ActivatedAt { get; set; }
|
||||
|
||||
public DateTime? DeactivatedAt { get; set; }
|
||||
|
||||
public bool WasForceActivated { get; set; }
|
||||
|
||||
public string? ForceActivateReason { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
|
||||
public partial class State
|
||||
{
|
||||
public string Id { get; set; } = null!;
|
||||
|
||||
public string TenantId { get; set; } = null!;
|
||||
|
||||
public bool Sealed { get; set; }
|
||||
|
||||
public string? PolicyHash { get; set; }
|
||||
|
||||
public string TimeAnchor { get; set; } = null!;
|
||||
|
||||
public DateTime LastTransitionAt { get; set; }
|
||||
|
||||
public string StalenessBudget { get; set; } = null!;
|
||||
|
||||
public long DriftBaselineSeconds { get; set; }
|
||||
|
||||
public string ContentBudgets { get; set; } = null!;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Npgsql;
|
||||
using StellaOps.AirGap.Persistence.EfCore.CompiledModels;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Context;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.Postgres;
|
||||
|
||||
internal static class AirGapDbContextFactory
|
||||
{
|
||||
public static AirGapDbContext Create(NpgsqlConnection connection, int commandTimeoutSeconds, string schemaName)
|
||||
{
|
||||
var normalizedSchema = string.IsNullOrWhiteSpace(schemaName)
|
||||
? AirGapDataSource.DefaultSchemaName
|
||||
: schemaName.Trim();
|
||||
|
||||
var optionsBuilder = new DbContextOptionsBuilder<AirGapDbContext>()
|
||||
.UseNpgsql(connection, npgsql => npgsql.CommandTimeout(commandTimeoutSeconds));
|
||||
|
||||
if (string.Equals(normalizedSchema, AirGapDataSource.DefaultSchemaName, StringComparison.Ordinal))
|
||||
{
|
||||
// Use the static compiled model module when schema mapping matches the default model.
|
||||
optionsBuilder.UseModel(AirGapDbContextModel.Instance);
|
||||
}
|
||||
|
||||
return new AirGapDbContext(optionsBuilder.Options, normalizedSchema);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,42 @@
|
||||
using Npgsql;
|
||||
using StellaOps.AirGap.Controller.Domain;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.Postgres.Repositories;
|
||||
|
||||
public sealed partial class PostgresAirGapStateStore
|
||||
{
|
||||
private AirGapState Map(NpgsqlDataReader reader)
|
||||
private AirGapState Map(State row)
|
||||
{
|
||||
var id = reader.GetString(0);
|
||||
var tenantId = reader.GetString(1);
|
||||
var sealed_ = reader.GetBoolean(2);
|
||||
var policyHash = reader.IsDBNull(3) ? null : reader.GetString(3);
|
||||
var timeAnchorJson = reader.GetFieldValue<string>(4);
|
||||
var lastTransitionAt = reader.GetFieldValue<DateTimeOffset>(5);
|
||||
var stalenessBudgetJson = reader.GetFieldValue<string>(6);
|
||||
var driftBaselineSeconds = reader.GetInt64(7);
|
||||
var contentBudgetsJson = reader.IsDBNull(8) ? null : reader.GetFieldValue<string>(8);
|
||||
|
||||
var timeAnchor = DeserializeTimeAnchor(timeAnchorJson);
|
||||
var stalenessBudget = DeserializeStalenessBudget(stalenessBudgetJson);
|
||||
var contentBudgets = DeserializeContentBudgets(contentBudgetsJson);
|
||||
var timeAnchor = DeserializeTimeAnchor(row.TimeAnchor);
|
||||
var stalenessBudget = DeserializeStalenessBudget(row.StalenessBudget);
|
||||
var contentBudgets = DeserializeContentBudgets(row.ContentBudgets);
|
||||
|
||||
return new AirGapState
|
||||
{
|
||||
Id = id,
|
||||
TenantId = tenantId,
|
||||
Sealed = sealed_,
|
||||
PolicyHash = policyHash,
|
||||
Id = row.Id,
|
||||
TenantId = row.TenantId,
|
||||
Sealed = row.Sealed,
|
||||
PolicyHash = row.PolicyHash,
|
||||
TimeAnchor = timeAnchor,
|
||||
LastTransitionAt = lastTransitionAt,
|
||||
LastTransitionAt = ToUtcOffset(row.LastTransitionAt),
|
||||
StalenessBudget = stalenessBudget,
|
||||
DriftBaselineSeconds = driftBaselineSeconds,
|
||||
DriftBaselineSeconds = row.DriftBaselineSeconds,
|
||||
ContentBudgets = contentBudgets
|
||||
};
|
||||
}
|
||||
|
||||
private static DateTimeOffset ToUtcOffset(DateTime value)
|
||||
{
|
||||
if (value.Kind == DateTimeKind.Utc)
|
||||
{
|
||||
return new DateTimeOffset(value, TimeSpan.Zero);
|
||||
}
|
||||
|
||||
if (value.Kind == DateTimeKind.Local)
|
||||
{
|
||||
return new DateTimeOffset(value.ToUniversalTime(), TimeSpan.Zero);
|
||||
}
|
||||
|
||||
return new DateTimeOffset(DateTime.SpecifyKind(value, DateTimeKind.Utc), TimeSpan.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Npgsql;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using StellaOps.AirGap.Controller.Domain;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.Postgres.Repositories;
|
||||
@@ -13,44 +11,33 @@ public sealed partial class PostgresAirGapStateStore
|
||||
await EnsureTableAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var tenantKey = NormalizeTenantId(tenantId);
|
||||
var stateTable = GetQualifiedTableName("state");
|
||||
|
||||
await using var connection = await DataSource.OpenConnectionAsync(tenantKey, "reader", cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
var sql = $$"""
|
||||
SELECT id, tenant_id, sealed, policy_hash, time_anchor, last_transition_at,
|
||||
staleness_budget, drift_baseline_seconds, content_budgets
|
||||
FROM {{stateTable}}
|
||||
WHERE tenant_id = @tenant_id;
|
||||
""";
|
||||
await using var dbContext = AirGapDbContextFactory.Create(connection, CommandTimeoutSeconds, GetSchemaName());
|
||||
|
||||
await using var command = CreateCommand(sql, connection);
|
||||
AddParameter(command, "tenant_id", tenantKey);
|
||||
var current = await dbContext.States
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(s => s.TenantId == tenantKey, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))
|
||||
if (current is not null)
|
||||
{
|
||||
if (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return Map(reader);
|
||||
}
|
||||
return Map(current);
|
||||
}
|
||||
|
||||
// Fallback for legacy rows stored without normalization.
|
||||
await using var fallbackCommand = CreateCommand($$"""
|
||||
SELECT id, tenant_id, sealed, policy_hash, time_anchor, last_transition_at,
|
||||
staleness_budget, drift_baseline_seconds, content_budgets
|
||||
FROM {{stateTable}}
|
||||
WHERE LOWER(tenant_id) = LOWER(@tenant_id)
|
||||
ORDER BY updated_at DESC, id DESC
|
||||
LIMIT 1;
|
||||
""", connection);
|
||||
AddParameter(fallbackCommand, "tenant_id", tenantId);
|
||||
|
||||
await using var fallbackReader = await fallbackCommand.ExecuteReaderAsync(cancellationToken)
|
||||
var lowerTenant = tenantId.Trim().ToLowerInvariant();
|
||||
var fallback = await dbContext.States
|
||||
.AsNoTracking()
|
||||
.Where(s => s.TenantId.ToLower() == lowerTenant)
|
||||
.OrderByDescending(s => s.UpdatedAt)
|
||||
.ThenByDescending(s => s.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (await fallbackReader.ReadAsync(cancellationToken).ConfigureAwait(false))
|
||||
|
||||
if (fallback is not null)
|
||||
{
|
||||
return Map(fallbackReader);
|
||||
return Map(fallback);
|
||||
}
|
||||
|
||||
return new AirGapState { TenantId = tenantId };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Npgsql;
|
||||
using StellaOps.AirGap.Controller.Domain;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.Postgres.Repositories;
|
||||
|
||||
@@ -12,42 +13,89 @@ public sealed partial class PostgresAirGapStateStore
|
||||
await EnsureTableAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var tenantKey = NormalizeTenantId(state.TenantId);
|
||||
var stateTable = GetQualifiedTableName("state");
|
||||
|
||||
await using var connection = await DataSource.OpenConnectionAsync(tenantKey, "writer", cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
var sql = $$"""
|
||||
INSERT INTO {{stateTable}} (
|
||||
id, tenant_id, sealed, policy_hash, time_anchor, last_transition_at,
|
||||
staleness_budget, drift_baseline_seconds, content_budgets
|
||||
)
|
||||
VALUES (
|
||||
@id, @tenant_id, @sealed, @policy_hash, @time_anchor, @last_transition_at,
|
||||
@staleness_budget, @drift_baseline_seconds, @content_budgets
|
||||
)
|
||||
ON CONFLICT (tenant_id) DO UPDATE SET
|
||||
id = EXCLUDED.id,
|
||||
sealed = EXCLUDED.sealed,
|
||||
policy_hash = EXCLUDED.policy_hash,
|
||||
time_anchor = EXCLUDED.time_anchor,
|
||||
last_transition_at = EXCLUDED.last_transition_at,
|
||||
staleness_budget = EXCLUDED.staleness_budget,
|
||||
drift_baseline_seconds = EXCLUDED.drift_baseline_seconds,
|
||||
content_budgets = EXCLUDED.content_budgets,
|
||||
updated_at = NOW();
|
||||
""";
|
||||
await using var dbContext = AirGapDbContextFactory.Create(connection, CommandTimeoutSeconds, GetSchemaName());
|
||||
|
||||
await using var command = CreateCommand(sql, connection);
|
||||
AddParameter(command, "id", state.Id);
|
||||
AddParameter(command, "tenant_id", tenantKey);
|
||||
AddParameter(command, "sealed", state.Sealed);
|
||||
AddParameter(command, "policy_hash", (object?)state.PolicyHash ?? DBNull.Value);
|
||||
AddJsonbParameter(command, "time_anchor", SerializeTimeAnchor(state.TimeAnchor));
|
||||
AddParameter(command, "last_transition_at", state.LastTransitionAt);
|
||||
AddJsonbParameter(command, "staleness_budget", SerializeStalenessBudget(state.StalenessBudget));
|
||||
AddParameter(command, "drift_baseline_seconds", state.DriftBaselineSeconds);
|
||||
AddJsonbParameter(command, "content_budgets", SerializeContentBudgets(state.ContentBudgets));
|
||||
var existing = await dbContext.States
|
||||
.FirstOrDefaultAsync(s => s.TenantId == tenantKey, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (existing is null)
|
||||
{
|
||||
dbContext.States.Add(ToEntity(state, tenantKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
Apply(existing, state, tenantKey);
|
||||
existing.UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (DbUpdateException ex) when (IsUniqueViolation(ex))
|
||||
{
|
||||
dbContext.ChangeTracker.Clear();
|
||||
|
||||
var conflict = await dbContext.States
|
||||
.FirstOrDefaultAsync(s => s.TenantId == tenantKey, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (conflict is null)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
Apply(conflict, state, tenantKey);
|
||||
conflict.UpdatedAt = DateTime.UtcNow;
|
||||
await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private State ToEntity(AirGapState state, string tenantKey)
|
||||
{
|
||||
return new State
|
||||
{
|
||||
Id = state.Id,
|
||||
TenantId = tenantKey,
|
||||
Sealed = state.Sealed,
|
||||
PolicyHash = state.PolicyHash,
|
||||
TimeAnchor = SerializeTimeAnchor(state.TimeAnchor),
|
||||
LastTransitionAt = state.LastTransitionAt.UtcDateTime,
|
||||
StalenessBudget = SerializeStalenessBudget(state.StalenessBudget),
|
||||
DriftBaselineSeconds = state.DriftBaselineSeconds,
|
||||
ContentBudgets = SerializeContentBudgets(state.ContentBudgets),
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
private void Apply(State entity, AirGapState state, string tenantKey)
|
||||
{
|
||||
entity.Id = state.Id;
|
||||
entity.TenantId = tenantKey;
|
||||
entity.Sealed = state.Sealed;
|
||||
entity.PolicyHash = state.PolicyHash;
|
||||
entity.TimeAnchor = SerializeTimeAnchor(state.TimeAnchor);
|
||||
entity.LastTransitionAt = state.LastTransitionAt.UtcDateTime;
|
||||
entity.StalenessBudget = SerializeStalenessBudget(state.StalenessBudget);
|
||||
entity.DriftBaselineSeconds = state.DriftBaselineSeconds;
|
||||
entity.ContentBudgets = SerializeContentBudgets(state.ContentBudgets);
|
||||
}
|
||||
|
||||
private static bool IsUniqueViolation(DbUpdateException exception)
|
||||
{
|
||||
Exception? current = exception;
|
||||
while (current is not null)
|
||||
{
|
||||
if (current is PostgresException { SqlState: PostgresErrorCodes.UniqueViolation })
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
current = current.InnerException;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,60 @@
|
||||
using System.Threading;
|
||||
using Npgsql;
|
||||
using StellaOps.AirGap.Importer.Versioning;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
using BundleVersionEntity = StellaOps.AirGap.Persistence.EfCore.Models.BundleVersion;
|
||||
using BundleVersionHistoryEntity = StellaOps.AirGap.Persistence.EfCore.Models.BundleVersionHistory;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.Postgres.Repositories;
|
||||
|
||||
public sealed partial class PostgresBundleVersionStore
|
||||
{
|
||||
private static BundleVersionRecord Map(NpgsqlDataReader reader)
|
||||
private static BundleVersionRecord Map(BundleVersionEntity row)
|
||||
{
|
||||
var tenantId = reader.GetString(0);
|
||||
var bundleType = reader.GetString(1);
|
||||
var versionString = reader.GetString(2);
|
||||
var major = reader.GetInt32(3);
|
||||
var minor = reader.GetInt32(4);
|
||||
var patch = reader.GetInt32(5);
|
||||
var prerelease = reader.IsDBNull(6) ? null : reader.GetString(6);
|
||||
var bundleCreatedAt = reader.GetFieldValue<DateTimeOffset>(7);
|
||||
var bundleDigest = reader.GetString(8);
|
||||
var activatedAt = reader.GetFieldValue<DateTimeOffset>(9);
|
||||
var wasForceActivated = reader.GetBoolean(10);
|
||||
var forceActivateReason = reader.IsDBNull(11) ? null : reader.GetString(11);
|
||||
|
||||
return new BundleVersionRecord(
|
||||
TenantId: tenantId,
|
||||
BundleType: bundleType,
|
||||
VersionString: versionString,
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
Patch: patch,
|
||||
Prerelease: prerelease,
|
||||
BundleCreatedAt: bundleCreatedAt,
|
||||
BundleDigest: bundleDigest,
|
||||
ActivatedAt: activatedAt,
|
||||
WasForceActivated: wasForceActivated,
|
||||
ForceActivateReason: forceActivateReason);
|
||||
TenantId: row.TenantId,
|
||||
BundleType: row.BundleType,
|
||||
VersionString: row.VersionString,
|
||||
Major: row.Major,
|
||||
Minor: row.Minor,
|
||||
Patch: row.Patch,
|
||||
Prerelease: row.Prerelease,
|
||||
BundleCreatedAt: ToUtcOffset(row.BundleCreatedAt),
|
||||
BundleDigest: row.BundleDigest,
|
||||
ActivatedAt: ToUtcOffset(row.ActivatedAt),
|
||||
WasForceActivated: row.WasForceActivated,
|
||||
ForceActivateReason: row.ForceActivateReason);
|
||||
}
|
||||
|
||||
private async Task<BundleVersionRecord?> GetCurrentForUpdateAsync(
|
||||
NpgsqlConnection connection,
|
||||
NpgsqlTransaction transaction,
|
||||
string versionTable,
|
||||
string tenantKey,
|
||||
string bundleTypeKey,
|
||||
CancellationToken ct)
|
||||
private static BundleVersionRecord Map(BundleVersionHistoryEntity row)
|
||||
{
|
||||
var sql = $$"""
|
||||
SELECT tenant_id, bundle_type, version_string, major, minor, patch, prerelease,
|
||||
bundle_created_at, bundle_digest, activated_at, was_force_activated, force_activate_reason
|
||||
FROM {{versionTable}}
|
||||
WHERE tenant_id = @tenant_id AND bundle_type = @bundle_type
|
||||
FOR UPDATE;
|
||||
""";
|
||||
|
||||
await using var command = CreateCommand(sql, connection);
|
||||
command.Transaction = transaction;
|
||||
AddParameter(command, "tenant_id", tenantKey);
|
||||
AddParameter(command, "bundle_type", bundleTypeKey);
|
||||
|
||||
await using var reader = await command.ExecuteReaderAsync(ct).ConfigureAwait(false);
|
||||
return await reader.ReadAsync(ct).ConfigureAwait(false) ? Map(reader) : null;
|
||||
return new BundleVersionRecord(
|
||||
TenantId: row.TenantId,
|
||||
BundleType: row.BundleType,
|
||||
VersionString: row.VersionString,
|
||||
Major: row.Major,
|
||||
Minor: row.Minor,
|
||||
Patch: row.Patch,
|
||||
Prerelease: row.Prerelease,
|
||||
BundleCreatedAt: ToUtcOffset(row.BundleCreatedAt),
|
||||
BundleDigest: row.BundleDigest,
|
||||
ActivatedAt: ToUtcOffset(row.ActivatedAt),
|
||||
WasForceActivated: row.WasForceActivated,
|
||||
ForceActivateReason: row.ForceActivateReason);
|
||||
}
|
||||
|
||||
private static DateTimeOffset ToUtcOffset(DateTime value)
|
||||
{
|
||||
if (value.Kind == DateTimeKind.Utc)
|
||||
{
|
||||
return new DateTimeOffset(value, TimeSpan.Zero);
|
||||
}
|
||||
|
||||
if (value.Kind == DateTimeKind.Local)
|
||||
{
|
||||
return new DateTimeOffset(value.ToUniversalTime(), TimeSpan.Zero);
|
||||
}
|
||||
|
||||
return new DateTimeOffset(DateTime.SpecifyKind(value, DateTimeKind.Utc), TimeSpan.Zero);
|
||||
}
|
||||
|
||||
private static DateTime ToUtcDateTime(DateTimeOffset value) => value.UtcDateTime;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Npgsql;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using StellaOps.AirGap.Importer.Versioning;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.Postgres.Repositories;
|
||||
@@ -21,21 +18,17 @@ public sealed partial class PostgresBundleVersionStore
|
||||
var tenantKey = NormalizeKey(tenantId);
|
||||
var bundleTypeKey = NormalizeKey(bundleType);
|
||||
|
||||
var versionTable = GetQualifiedTableName("bundle_versions");
|
||||
await using var connection = await DataSource.OpenConnectionAsync(tenantKey, "reader", ct).ConfigureAwait(false);
|
||||
var sql = $$"""
|
||||
SELECT tenant_id, bundle_type, version_string, major, minor, patch, prerelease,
|
||||
bundle_created_at, bundle_digest, activated_at, was_force_activated, force_activate_reason
|
||||
FROM {{versionTable}}
|
||||
WHERE tenant_id = @tenant_id AND bundle_type = @bundle_type;
|
||||
""";
|
||||
await using var dbContext = AirGapDbContextFactory.Create(connection, CommandTimeoutSeconds, GetSchemaName());
|
||||
|
||||
await using var command = CreateCommand(sql, connection);
|
||||
AddParameter(command, "tenant_id", tenantKey);
|
||||
AddParameter(command, "bundle_type", bundleTypeKey);
|
||||
var row = await dbContext.BundleVersions
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(
|
||||
b => b.TenantId == tenantKey && b.BundleType == bundleTypeKey,
|
||||
ct)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await using var reader = await command.ExecuteReaderAsync(ct).ConfigureAwait(false);
|
||||
return await reader.ReadAsync(ct).ConfigureAwait(false) ? Map(reader) : null;
|
||||
return row is null ? null : Map(row);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<BundleVersionRecord>> GetHistoryAsync(
|
||||
@@ -57,29 +50,18 @@ public sealed partial class PostgresBundleVersionStore
|
||||
var tenantKey = NormalizeKey(tenantId);
|
||||
var bundleTypeKey = NormalizeKey(bundleType);
|
||||
|
||||
var historyTable = GetQualifiedTableName("bundle_version_history");
|
||||
await using var connection = await DataSource.OpenConnectionAsync(tenantKey, "reader", ct).ConfigureAwait(false);
|
||||
var sql = $$"""
|
||||
SELECT tenant_id, bundle_type, version_string, major, minor, patch, prerelease,
|
||||
bundle_created_at, bundle_digest, activated_at, was_force_activated, force_activate_reason
|
||||
FROM {{historyTable}}
|
||||
WHERE tenant_id = @tenant_id AND bundle_type = @bundle_type
|
||||
ORDER BY activated_at DESC, id DESC
|
||||
LIMIT @limit;
|
||||
""";
|
||||
await using var dbContext = AirGapDbContextFactory.Create(connection, CommandTimeoutSeconds, GetSchemaName());
|
||||
|
||||
await using var command = CreateCommand(sql, connection);
|
||||
AddParameter(command, "tenant_id", tenantKey);
|
||||
AddParameter(command, "bundle_type", bundleTypeKey);
|
||||
AddParameter(command, "limit", limit);
|
||||
var rows = await dbContext.BundleVersionHistories
|
||||
.AsNoTracking()
|
||||
.Where(b => b.TenantId == tenantKey && b.BundleType == bundleTypeKey)
|
||||
.OrderByDescending(b => b.ActivatedAt)
|
||||
.ThenByDescending(b => b.Id)
|
||||
.Take(limit)
|
||||
.ToListAsync(ct)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await using var reader = await command.ExecuteReaderAsync(ct).ConfigureAwait(false);
|
||||
var results = new List<BundleVersionRecord>();
|
||||
while (await reader.ReadAsync(ct).ConfigureAwait(false))
|
||||
{
|
||||
results.Add(Map(reader));
|
||||
}
|
||||
|
||||
return results;
|
||||
return rows.Select(Map).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,49 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Npgsql;
|
||||
using StellaOps.AirGap.Importer.Versioning;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Context;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
using BundleVersionEntity = StellaOps.AirGap.Persistence.EfCore.Models.BundleVersion;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.Postgres.Repositories;
|
||||
|
||||
public sealed partial class PostgresBundleVersionStore
|
||||
{
|
||||
private async Task UpsertCurrentAsync(
|
||||
NpgsqlConnection connection,
|
||||
NpgsqlTransaction tx,
|
||||
string versionTable,
|
||||
private static void UpsertCurrent(
|
||||
AirGapDbContext dbContext,
|
||||
BundleVersionEntity? currentEntity,
|
||||
BundleVersionRecord record,
|
||||
string tenantKey,
|
||||
string bundleTypeKey,
|
||||
CancellationToken ct)
|
||||
string bundleTypeKey)
|
||||
{
|
||||
var upsertSql = $$"""
|
||||
INSERT INTO {{versionTable}} (
|
||||
tenant_id, bundle_type, version_string, major, minor, patch, prerelease,
|
||||
bundle_created_at, bundle_digest, activated_at, was_force_activated, force_activate_reason
|
||||
)
|
||||
VALUES (
|
||||
@tenant_id, @bundle_type, @version_string, @major, @minor, @patch, @prerelease,
|
||||
@bundle_created_at, @bundle_digest, @activated_at, @was_force_activated, @force_activate_reason
|
||||
)
|
||||
ON CONFLICT (tenant_id, bundle_type) DO UPDATE SET
|
||||
version_string = EXCLUDED.version_string,
|
||||
major = EXCLUDED.major,
|
||||
minor = EXCLUDED.minor,
|
||||
patch = EXCLUDED.patch,
|
||||
prerelease = EXCLUDED.prerelease,
|
||||
bundle_created_at = EXCLUDED.bundle_created_at,
|
||||
bundle_digest = EXCLUDED.bundle_digest,
|
||||
activated_at = EXCLUDED.activated_at,
|
||||
was_force_activated = EXCLUDED.was_force_activated,
|
||||
force_activate_reason = EXCLUDED.force_activate_reason,
|
||||
updated_at = NOW();
|
||||
""";
|
||||
if (currentEntity is null)
|
||||
{
|
||||
dbContext.BundleVersions.Add(new BundleVersionEntity
|
||||
{
|
||||
TenantId = tenantKey,
|
||||
BundleType = bundleTypeKey,
|
||||
VersionString = record.VersionString,
|
||||
Major = record.Major,
|
||||
Minor = record.Minor,
|
||||
Patch = record.Patch,
|
||||
Prerelease = record.Prerelease,
|
||||
BundleCreatedAt = ToUtcDateTime(record.BundleCreatedAt),
|
||||
BundleDigest = record.BundleDigest,
|
||||
ActivatedAt = ToUtcDateTime(record.ActivatedAt),
|
||||
WasForceActivated = record.WasForceActivated,
|
||||
ForceActivateReason = record.ForceActivateReason
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await using var upsertCmd = CreateCommand(upsertSql, connection);
|
||||
upsertCmd.Transaction = tx;
|
||||
AddParameter(upsertCmd, "tenant_id", tenantKey);
|
||||
AddParameter(upsertCmd, "bundle_type", bundleTypeKey);
|
||||
AddParameter(upsertCmd, "version_string", record.VersionString);
|
||||
AddParameter(upsertCmd, "major", record.Major);
|
||||
AddParameter(upsertCmd, "minor", record.Minor);
|
||||
AddParameter(upsertCmd, "patch", record.Patch);
|
||||
AddParameter(upsertCmd, "prerelease", (object?)record.Prerelease ?? DBNull.Value);
|
||||
AddParameter(upsertCmd, "bundle_created_at", record.BundleCreatedAt);
|
||||
AddParameter(upsertCmd, "bundle_digest", record.BundleDigest);
|
||||
AddParameter(upsertCmd, "activated_at", record.ActivatedAt);
|
||||
AddParameter(upsertCmd, "was_force_activated", record.WasForceActivated);
|
||||
AddParameter(upsertCmd, "force_activate_reason", (object?)record.ForceActivateReason ?? DBNull.Value);
|
||||
await upsertCmd.ExecuteNonQueryAsync(ct).ConfigureAwait(false);
|
||||
currentEntity.VersionString = record.VersionString;
|
||||
currentEntity.Major = record.Major;
|
||||
currentEntity.Minor = record.Minor;
|
||||
currentEntity.Patch = record.Patch;
|
||||
currentEntity.Prerelease = record.Prerelease;
|
||||
currentEntity.BundleCreatedAt = ToUtcDateTime(record.BundleCreatedAt);
|
||||
currentEntity.BundleDigest = record.BundleDigest;
|
||||
currentEntity.ActivatedAt = ToUtcDateTime(record.ActivatedAt);
|
||||
currentEntity.WasForceActivated = record.WasForceActivated;
|
||||
currentEntity.ForceActivateReason = record.ForceActivateReason;
|
||||
currentEntity.UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +1,56 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Npgsql;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using StellaOps.AirGap.Importer.Versioning;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Context;
|
||||
using StellaOps.AirGap.Persistence.EfCore.Models;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.Postgres.Repositories;
|
||||
|
||||
public sealed partial class PostgresBundleVersionStore
|
||||
{
|
||||
private async Task CloseHistoryAsync(
|
||||
NpgsqlConnection connection,
|
||||
NpgsqlTransaction tx,
|
||||
string historyTable,
|
||||
private static async Task CloseHistoryAsync(
|
||||
AirGapDbContext dbContext,
|
||||
BundleVersionRecord record,
|
||||
string tenantKey,
|
||||
string bundleTypeKey,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var closeHistorySql = $$"""
|
||||
UPDATE {{historyTable}}
|
||||
SET deactivated_at = @activated_at
|
||||
WHERE tenant_id = @tenant_id AND bundle_type = @bundle_type AND deactivated_at IS NULL;
|
||||
""";
|
||||
var activeRows = await dbContext.BundleVersionHistories
|
||||
.Where(h => h.TenantId == tenantKey && h.BundleType == bundleTypeKey && h.DeactivatedAt == null)
|
||||
.ToListAsync(ct)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await using var closeCmd = CreateCommand(closeHistorySql, connection);
|
||||
closeCmd.Transaction = tx;
|
||||
AddParameter(closeCmd, "tenant_id", tenantKey);
|
||||
AddParameter(closeCmd, "bundle_type", bundleTypeKey);
|
||||
AddParameter(closeCmd, "activated_at", record.ActivatedAt);
|
||||
await closeCmd.ExecuteNonQueryAsync(ct).ConfigureAwait(false);
|
||||
if (activeRows.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var deactivatedAt = ToUtcDateTime(record.ActivatedAt);
|
||||
foreach (var row in activeRows)
|
||||
{
|
||||
row.DeactivatedAt = deactivatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InsertHistoryAsync(
|
||||
NpgsqlConnection connection,
|
||||
NpgsqlTransaction tx,
|
||||
string historyTable,
|
||||
private static void InsertHistory(
|
||||
AirGapDbContext dbContext,
|
||||
BundleVersionRecord record,
|
||||
string tenantKey,
|
||||
string bundleTypeKey,
|
||||
CancellationToken ct)
|
||||
string bundleTypeKey)
|
||||
{
|
||||
var historySql = $$"""
|
||||
INSERT INTO {{historyTable}} (
|
||||
tenant_id, bundle_type, version_string, major, minor, patch, prerelease,
|
||||
bundle_created_at, bundle_digest, activated_at, deactivated_at,
|
||||
was_force_activated, force_activate_reason
|
||||
)
|
||||
VALUES (
|
||||
@tenant_id, @bundle_type, @version_string, @major, @minor, @patch, @prerelease,
|
||||
@bundle_created_at, @bundle_digest, @activated_at, NULL,
|
||||
@was_force_activated, @force_activate_reason
|
||||
);
|
||||
""";
|
||||
|
||||
await using var historyCmd = CreateCommand(historySql, connection);
|
||||
historyCmd.Transaction = tx;
|
||||
AddParameter(historyCmd, "tenant_id", tenantKey);
|
||||
AddParameter(historyCmd, "bundle_type", bundleTypeKey);
|
||||
AddParameter(historyCmd, "version_string", record.VersionString);
|
||||
AddParameter(historyCmd, "major", record.Major);
|
||||
AddParameter(historyCmd, "minor", record.Minor);
|
||||
AddParameter(historyCmd, "patch", record.Patch);
|
||||
AddParameter(historyCmd, "prerelease", (object?)record.Prerelease ?? DBNull.Value);
|
||||
AddParameter(historyCmd, "bundle_created_at", record.BundleCreatedAt);
|
||||
AddParameter(historyCmd, "bundle_digest", record.BundleDigest);
|
||||
AddParameter(historyCmd, "activated_at", record.ActivatedAt);
|
||||
AddParameter(historyCmd, "was_force_activated", record.WasForceActivated);
|
||||
AddParameter(historyCmd, "force_activate_reason", (object?)record.ForceActivateReason ?? DBNull.Value);
|
||||
await historyCmd.ExecuteNonQueryAsync(ct).ConfigureAwait(false);
|
||||
dbContext.BundleVersionHistories.Add(new BundleVersionHistory
|
||||
{
|
||||
TenantId = tenantKey,
|
||||
BundleType = bundleTypeKey,
|
||||
VersionString = record.VersionString,
|
||||
Major = record.Major,
|
||||
Minor = record.Minor,
|
||||
Patch = record.Patch,
|
||||
Prerelease = record.Prerelease,
|
||||
BundleCreatedAt = ToUtcDateTime(record.BundleCreatedAt),
|
||||
BundleDigest = record.BundleDigest,
|
||||
ActivatedAt = ToUtcDateTime(record.ActivatedAt),
|
||||
WasForceActivated = record.WasForceActivated,
|
||||
ForceActivateReason = record.ForceActivateReason
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using StellaOps.AirGap.Importer.Versioning;
|
||||
|
||||
namespace StellaOps.AirGap.Persistence.Postgres.Repositories;
|
||||
@@ -14,30 +14,25 @@ public sealed partial class PostgresBundleVersionStore
|
||||
var tenantKey = NormalizeKey(record.TenantId);
|
||||
var bundleTypeKey = NormalizeKey(record.BundleType);
|
||||
|
||||
var versionTable = GetQualifiedTableName("bundle_versions");
|
||||
var historyTable = GetQualifiedTableName("bundle_version_history");
|
||||
|
||||
await using var connection = await DataSource.OpenConnectionAsync(tenantKey, "writer", ct).ConfigureAwait(false);
|
||||
await using var tx = await connection.BeginTransactionAsync(ct).ConfigureAwait(false);
|
||||
await using var dbContext = AirGapDbContextFactory.Create(connection, CommandTimeoutSeconds, GetSchemaName());
|
||||
await using var tx = await dbContext.Database.BeginTransactionAsync(IsolationLevel.Serializable, ct)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var current = await GetCurrentForUpdateAsync(
|
||||
connection,
|
||||
tx,
|
||||
versionTable,
|
||||
tenantKey,
|
||||
bundleTypeKey,
|
||||
var currentEntity = await dbContext.BundleVersions
|
||||
.FirstOrDefaultAsync(
|
||||
b => b.TenantId == tenantKey && b.BundleType == bundleTypeKey,
|
||||
ct)
|
||||
.ConfigureAwait(false);
|
||||
var current = currentEntity is null ? null : Map(currentEntity);
|
||||
|
||||
EnsureMonotonicVersion(record, current);
|
||||
|
||||
await CloseHistoryAsync(connection, tx, historyTable, record, tenantKey, bundleTypeKey, ct)
|
||||
.ConfigureAwait(false);
|
||||
await InsertHistoryAsync(connection, tx, historyTable, record, tenantKey, bundleTypeKey, ct)
|
||||
.ConfigureAwait(false);
|
||||
await UpsertCurrentAsync(connection, tx, versionTable, record, tenantKey, bundleTypeKey, ct)
|
||||
.ConfigureAwait(false);
|
||||
await CloseHistoryAsync(dbContext, record, tenantKey, bundleTypeKey, ct).ConfigureAwait(false);
|
||||
InsertHistory(dbContext, record, tenantKey, bundleTypeKey);
|
||||
UpsertCurrent(dbContext, currentEntity, record, tenantKey, bundleTypeKey);
|
||||
|
||||
await dbContext.SaveChangesAsync(ct).ConfigureAwait(false);
|
||||
await tx.CommitAsync(ct).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
<EmbeddedResource Include="Migrations\**\*.sql" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Prevent automatic compiled-model binding so non-default schemas can build runtime models. -->
|
||||
<Compile Remove="EfCore\CompiledModels\AirGapDbContextAssemblyAttributes.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" PrivateAssets="all" />
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
# StellaOps.AirGap.Persistence Task Board
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_solid_review.md`.
|
||||
Source of truth:
|
||||
- `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_solid_review.md`
|
||||
- `docs/implplan/SPRINT_20260222_064_AirGap_next_smallest_module_dal_to_efcore.md`
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| REMED-05 | DONE | Remediation checklist: docs/implplan/audits/csproj-standards/remediation/checklists/src/AirGap/__Libraries/StellaOps.AirGap.Persistence/StellaOps.AirGap.Persistence.md. |
|
||||
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
|
||||
| AIRGAP-EF-01 | DONE | Scaffolded EF models/context for AirGap schema (`state`, `bundle_versions`, `bundle_version_history`). |
|
||||
| AIRGAP-EF-02 | DONE | Converted `PostgresAirGapStateStore` and `PostgresBundleVersionStore` DAL flows to EF Core with preserved contracts. |
|
||||
| AIRGAP-EF-03 | DONE | Added compiled model generation and static model runtime wiring for default `airgap` schema. |
|
||||
| AIRGAP-EF-04 | DONE | Completed sequential build/test + docs updates for AirGap EF migration workflow. |
|
||||
|
||||
Reference in New Issue
Block a user