diff --git a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Configurations/AuditLogEntityTypeConfiguration.cs b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Configurations/AuditLogEntityTypeConfiguration.cs
index 348ef385..217686ad 100644
--- a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Configurations/AuditLogEntityTypeConfiguration.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Configurations/AuditLogEntityTypeConfiguration.cs
@@ -6,11 +6,12 @@ using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Entities;
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Configurations;
///
-/// Maps the persistence shape to the central AuditLog
-/// table described in alog.md §4. Column lengths/types and the named indexes are
-/// fixed by that specification — keep this in sync with the doc. C3 (Task 2.5) kept
-/// the table unchanged; the canonical record is mapped onto this row at the repository
-/// boundary via AuditRowProjection.
+/// Maps the C5 (Task 2.5) persistence shape to the central
+/// dbo.AuditLog table: the 10 canonical ZB.MOM.WW.Audit.AuditEvent columns
+/// (writable) plus six read-only, server-side persisted computed columns derived
+/// from DetailsJson via JSON_VALUE. The computed-column SQL and the index
+/// set here mirror the CollapseAuditLogToCanonical migration's
+/// dbo.AuditLog_v2 DDL byte-for-byte — keep them in sync.
///
public class AuditLogEntityTypeConfiguration : IEntityTypeConfiguration
{
@@ -35,88 +36,146 @@ public class AuditLogEntityTypeConfiguration : IEntityTypeConfiguration v.HasValue ? DateTime.SpecifyKind(v.Value, DateTimeKind.Utc) : null);
+ // The computed columns derive enum-named strings from DetailsJson (e.g.
+ // JSON_VALUE(...,'$.kind') == "CachedResolve"), exactly the value
+ // HasConversion() expects on read. The repository never writes these
+ // (they are server-computed), but the string<->enum converter is still
+ // required so EF materialises them as the strongly-typed enum a LINQ
+ // predicate like `e.Kind == AuditKind.CachedResolve` translates against.
+
/// Applies the EF Core type configuration for to the model builder.
/// The entity type builder to configure.
public void Configure(EntityTypeBuilder builder)
{
builder.ToTable("AuditLog");
- // Enforce DateTimeKind.Utc on every *Utc-suffixed DateTime column. See
- // the UtcConverter remarks above for the rationale.
- builder.Property(e => e.OccurredAtUtc).HasConversion(UtcConverter);
- builder.Property(e => e.IngestedAtUtc).HasConversion(NullableUtcConverter);
+ // ── Canonical columns (writable) ─────────────────────────────────────
+ //
+ // Column SQL TYPES are intentionally left to EF's relational conventions
+ // (driven by HasMaxLength / IsUnicode / the CLR type) rather than pinned
+ // with HasColumnType, so the SAME configuration maps to SQL Server
+ // (varchar(n) / nvarchar(n) / uniqueidentifier / datetime2) in production
+ // AND to the SQLite test provider (TEXT) without a `(max)`/`uniqueidentifier`
+ // literal leaking into SQLite DDL. The migration's raw DDL pins the exact
+ // SQL Server types; EF's conventions agree with them (verified clean via
+ // `has-pending-model-changes`).
- // Composite PK includes OccurredAtUtc — required by the monthly partition scheme
- // (ps_AuditLog_Month) so the clustered key is partition-aligned. EventId still
- // needs to be globally unique for InsertIfNotExistsAsync idempotency, so a
- // separate unique index is declared on EventId alone.
- builder.HasKey(e => new { e.EventId, e.OccurredAtUtc });
-
- builder.HasIndex(e => e.EventId)
- .IsUnique()
- .HasDatabaseName("UX_AuditLog_EventId");
-
- // Enum-as-string columns: bounded varchar(32) ASCII.
- builder.Property(e => e.Channel)
- .HasConversion()
- .HasMaxLength(32)
- .IsUnicode(false)
- .IsRequired();
-
- builder.Property(e => e.Kind)
- .HasConversion()
- .HasMaxLength(32)
- .IsUnicode(false)
- .IsRequired();
-
- builder.Property(e => e.Status)
- .HasConversion()
- .HasMaxLength(32)
- .IsUnicode(false)
- .IsRequired();
-
- builder.Property(e => e.ForwardState)
- .HasConversion()
- .HasMaxLength(32)
- .IsUnicode(false);
-
- // Ascii identifier columns — never carry user-supplied unicode.
- builder.Property(e => e.SourceSiteId)
- .HasMaxLength(64)
- .IsUnicode(false);
-
- builder.Property(e => e.SourceInstanceId)
- .HasMaxLength(128)
- .IsUnicode(false);
-
- builder.Property(e => e.SourceScript)
- .HasMaxLength(128)
- .IsUnicode(false);
+ // Enforce DateTimeKind.Utc on the OccurredAtUtc column. See the
+ // UtcConverter remarks above for the rationale.
+ builder.Property(e => e.OccurredAtUtc)
+ .HasConversion(UtcConverter);
builder.Property(e => e.Actor)
- .HasMaxLength(128)
- .IsUnicode(false);
+ .HasMaxLength(256);
+
+ builder.Property(e => e.Action)
+ .HasMaxLength(64)
+ .IsUnicode(false)
+ .IsRequired();
+
+ builder.Property(e => e.Outcome)
+ .HasConversion()
+ .HasMaxLength(16)
+ .IsUnicode(false)
+ .IsRequired();
+
+ // Channel rides in the canonical Category column (Category = channel name
+ // for ScadaBridge). Stored as the enum's name in varchar(32); the
+ // string<->enum converter lets `e.Channel == AuditChannel.X` translate to
+ // `[Category] = 'X'` server-side.
+ builder.Property(e => e.Channel)
+ .HasColumnName("Category")
+ .HasConversion()
+ .HasMaxLength(32)
+ .IsUnicode(false)
+ .IsRequired();
builder.Property(e => e.Target)
- .HasMaxLength(256)
- .IsUnicode(false);
+ .HasMaxLength(256);
// SourceNode (Audit Log #23, SourceNode-stamping): node-local identifier of the
// cluster member that produced the row (e.g. "node-a", "central-a"). NULL is
// valid for reconciled rows from a retired node and for direct-write rows
// produced before this feature shipped. ASCII — varchar(64), no unicode.
builder.Property(e => e.SourceNode)
- .HasColumnType("varchar(64)")
.HasMaxLength(64)
.IsUnicode(false);
- // Bounded unicode message column.
- builder.Property(e => e.ErrorMessage)
- .HasMaxLength(1024);
+ // DetailsJson: unbounded → nvarchar(max) on SQL Server, TEXT on SQLite.
+ // (No HasMaxLength / HasColumnType — let conventions pick per provider.)
- // ErrorDetail, RequestSummary, ResponseSummary, Extra: leave as nvarchar(max).
+ // ── Persisted computed columns (read-only; derived from DetailsJson) ──
+ //
+ // Each is `… AS PERSISTED`; EF must never attempt to write them
+ // (ValueGeneratedOnAddOrUpdate + metadata-only mapping). The SQL strings
+ // here MUST match the migration's dbo.AuditLog_v2 DDL exactly so
+ // `dotnet ef migrations has-pending-model-changes` stays clean. The SQLite
+ // test context strips the computed-column SQL (JSON_VALUE is unknown to
+ // SQLite) so EnsureCreated still works.
- // Indexes — names locked to alog.md §4 for reconciliation/migration discoverability.
+ builder.Property(e => e.Kind)
+ .HasConversion()
+ .HasMaxLength(32)
+ .IsUnicode(false)
+ .HasComputedColumnSql("JSON_VALUE(DetailsJson,'$.kind')", stored: true)
+ .ValueGeneratedOnAddOrUpdate()
+ .IsRequired();
+
+ builder.Property(e => e.Status)
+ .HasConversion()
+ .HasMaxLength(32)
+ .IsUnicode(false)
+ .HasComputedColumnSql("JSON_VALUE(DetailsJson,'$.status')", stored: true)
+ .ValueGeneratedOnAddOrUpdate()
+ .IsRequired();
+
+ builder.Property(e => e.SourceSiteId)
+ .HasMaxLength(64)
+ .IsUnicode(false)
+ .HasComputedColumnSql("JSON_VALUE(DetailsJson,'$.sourceSiteId')", stored: true)
+ .ValueGeneratedOnAddOrUpdate();
+
+ builder.Property(e => e.ExecutionId)
+ .HasComputedColumnSql("CAST(JSON_VALUE(DetailsJson,'$.executionId') AS uniqueidentifier)", stored: true)
+ .ValueGeneratedOnAddOrUpdate();
+
+ builder.Property(e => e.ParentExecutionId)
+ .HasComputedColumnSql("CAST(JSON_VALUE(DetailsJson,'$.parentExecutionId') AS uniqueidentifier)", stored: true)
+ .ValueGeneratedOnAddOrUpdate();
+
+ // IngestedAtUtc rides in DetailsJson as an ISO-8601-with-offset string
+ // (always +00:00 — the codec normalises to UTC). SWITCHOFFSET(...,0)
+ // normalises any offset to UTC before the datetime2 cast, so the column
+ // is the UTC wall-clock regardless. The datetimeoffset cast / SWITCHOFFSET
+ // is NON-DETERMINISTic to SQL Server, so this computed column is NOT
+ // persisted (stored:false) — a PERSISTED non-deterministic column is
+ // rejected at CREATE. It is not indexed, so non-persistence costs nothing.
+ // Routed through the nullable UTC converter so the materialised value
+ // carries Kind=Utc.
+ builder.Property(e => e.IngestedAtUtc)
+ .HasColumnType("datetime2(7)")
+ .HasConversion(NullableUtcConverter)
+ .HasComputedColumnSql(
+ "CAST(SWITCHOFFSET(CAST(JSON_VALUE(DetailsJson,'$.ingestedAtUtc') AS datetimeoffset), 0) AS datetime2(7))",
+ stored: false)
+ .ValueGeneratedOnAddOrUpdate();
+
+ // ── Keys + indexes ───────────────────────────────────────────────────
+
+ // Composite PK includes OccurredAtUtc — required by the monthly partition scheme
+ // (ps_AuditLog_Month) so the clustered key is partition-aligned. EventId still
+ // needs to be globally unique for InsertIfNotExistsAsync idempotency, so a
+ // separate (non-aligned) unique index is declared on EventId alone.
+ builder.HasKey(e => new { e.EventId, e.OccurredAtUtc });
+
+ builder.HasIndex(e => e.EventId)
+ .IsUnique()
+ .HasDatabaseName("UX_AuditLog_EventId");
+
+ // Index names are locked for reconciliation/migration discoverability. The
+ // column SETS migrate to the canonical/computed shape (alog.md §4 semantics
+ // preserved): Channel→Category, Site/Node/Execution/ParentExecution now read
+ // off the computed columns.
builder.HasIndex(e => e.OccurredAtUtc)
.IsDescending(true)
.HasDatabaseName("IX_AuditLog_OccurredAtUtc");
@@ -129,22 +188,22 @@ public class AuditLogEntityTypeConfiguration : IEntityTypeConfiguration e.ExecutionId)
- .HasFilter("[ExecutionId] IS NOT NULL")
.HasDatabaseName("IX_AuditLog_Execution");
builder.HasIndex(e => e.ParentExecutionId)
- .HasFilter("[ParentExecutionId] IS NOT NULL")
.HasDatabaseName("IX_AuditLog_ParentExecution");
- // SourceNode composite index (Audit Log #23, SourceNode-stamping): backs
- // per-node Central UI / health-dashboard queries (e.g. "rows produced by
- // central-a, newest first"). Created via raw SQL in the migration so it lands
- // on the ps_AuditLog_Month(OccurredAtUtc) partition scheme like every other
- // IX_AuditLog_* index — keeps the partition-switch purge path intact.
builder.HasIndex(e => new { e.SourceNode, e.OccurredAtUtc })
.HasDatabaseName("IX_AuditLog_Node_Occurred");
+ // IX_AuditLog_Channel_Status_Occurred name preserved; columns are now the
+ // canonical Category (= channel) + computed Status + OccurredAtUtc.
builder.HasIndex(e => new { e.Channel, e.Status, e.OccurredAtUtc })
.IsDescending(false, false, true)
.HasDatabaseName("IX_AuditLog_Channel_Status_Occurred");
diff --git a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Entities/AuditLogRow.cs b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Entities/AuditLogRow.cs
index 7589d2d3..c4d8c345 100644
--- a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Entities/AuditLogRow.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Entities/AuditLogRow.cs
@@ -1,20 +1,41 @@
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
+using AuditOutcome = ZB.MOM.WW.Audit.AuditOutcome;
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Entities;
///
-/// Transitional EF Core persistence shape for the central dbo.AuditLog table
-/// (Audit Log #23). This is the 24-column row formerly modelled by
-/// ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.AuditEvent; in C3 (Task 2.5)
-/// the canonical ZB.MOM.WW.Audit.AuditEvent became the type at every seam,
-/// emit site, DTO boundary, and redactor, and this row type was relocated here as a
-/// storage-only entity so the existing table keeps working unchanged.
+/// EF Core persistence shape for the central dbo.AuditLog table after the
+/// C5 collapse (Audit Log #23, Task 2.5). The table is now the 10 canonical
+/// ZB.MOM.WW.Audit.AuditEvent fields stored DIRECTLY plus a set of
+/// read-only, server-side persisted computed columns derived from
+/// (JSON_VALUE … PERSISTED) so the
+/// reporting queries stay indexable without re-parsing JSON.
///
///
///
-/// The repository maps canonical ⇄ this row at the persistence boundary via
-/// ZB.MOM.WW.ScadaBridge.Commons.Types.Audit.AuditRowProjection. C5 replaces
-/// this shim + table with the real DetailsJson-backed schema.
+/// C5 (Task 2.5). The transitional 24-typed-column shim is retired. The
+/// repository writes the 10 canonical columns directly (no Decompose) and
+/// the computed columns auto-derive at INSERT; reads build the canonical
+/// AuditEvent straight off the canonical columns (no Recompose).
+///
+///
+/// Canonical columns (writable): ,
+/// , , ,
+/// , (the canonical Category
+/// column — for ScadaBridge, Category = channel name), ,
+/// , ,
+/// .
+///
+///
+/// Persisted computed columns (read-only): ,
+/// , , ,
+/// (the five spec'd queryability columns), plus
+/// (central ingest timestamp, also a DetailsJson
+/// field). These are populated by SQL Server from ; EF
+/// never writes them. Their getters expose them as typed
+/// (enum / / ) properties so the
+/// existing LINQ filter/aggregate queries keep their meaning; the value
+/// converters that turn enum names ⇄ varchar match the JSON_VALUE string output.
///
///
/// All *Utc-suffixed properties are invariantly UTC
@@ -27,6 +48,8 @@ namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Entities;
///
public sealed record AuditLogRow
{
+ // ── Canonical columns (the 10 ZB.MOM.WW.Audit.AuditEvent fields) ──────────
+
/// Idempotency key; uniquely identifies one audit lifecycle event.
public Guid EventId { get; init; }
@@ -38,7 +61,75 @@ public sealed record AuditLogRow
}
private readonly DateTime _occurredAtUtc;
- /// UTC timestamp when the row was ingested at central; null on the site hot-path.
+ /// Authenticated actor for inbound paths (API key name, user, etc.); null/empty for system/anon.
+ public string? Actor { get; init; }
+
+ /// Canonical action verb — "{channel}.{kind}" (e.g. ApiOutbound.ApiCall).
+ public string Action { get; init; } = string.Empty;
+
+ /// Normalized canonical outcome (Success / Failure / Denied).
+ public AuditOutcome Outcome { get; init; }
+
+ ///
+ /// Trust-boundary channel the audited action crossed. Stored in the canonical
+ /// Category column (for ScadaBridge the canonical Category IS the channel
+ /// name); exposed here as the strongly-typed enum.
+ ///
+ public AuditChannel Channel { get; init; }
+
+ /// Target of the action: external system name, db connection name, list name, or inbound method.
+ public string? Target { get; init; }
+
+ /// The cluster node on which the event was emitted.
+ public string? SourceNode { get; init; }
+
+ /// Correlation id linking related audit rows (e.g. the cached-op lifecycle).
+ public Guid? CorrelationId { get; init; }
+
+ /// Canonical JSON extension bag carrying every ScadaBridge domain field.
+ public string? DetailsJson { get; init; }
+
+ // ── Persisted computed columns (read-only; derived from DetailsJson) ──────
+
+ ///
+ /// Specific event kind. Computed column JSON_VALUE(DetailsJson,'$.kind')
+ /// PERSISTED; read-only (the DB derives it on INSERT).
+ ///
+ public AuditKind Kind { get; init; }
+
+ ///
+ /// Lifecycle status. Computed column JSON_VALUE(DetailsJson,'$.status')
+ /// PERSISTED; read-only.
+ ///
+ public AuditStatus Status { get; init; }
+
+ ///
+ /// Site id where the action originated; null for central-direct events. Computed
+ /// column JSON_VALUE(DetailsJson,'$.sourceSiteId') PERSISTED; read-only.
+ ///
+ public string? SourceSiteId { get; init; }
+
+ ///
+ /// Id of the originating script execution / inbound request. Computed column
+ /// CAST(JSON_VALUE(DetailsJson,'$.executionId') AS uniqueidentifier)
+ /// PERSISTED; read-only.
+ ///
+ public Guid? ExecutionId { get; init; }
+
+ ///
+ /// ExecutionId of the execution that spawned this run; null for top-level runs.
+ /// Computed column
+ /// CAST(JSON_VALUE(DetailsJson,'$.parentExecutionId') AS uniqueidentifier)
+ /// PERSISTED; read-only.
+ ///
+ public Guid? ParentExecutionId { get; init; }
+
+ ///
+ /// UTC timestamp when the central AuditLog store ingested this row; null until
+ /// central stamps it. Computed column over
+ /// JSON_VALUE(DetailsJson,'$.ingestedAtUtc') (normalized to UTC datetime2)
+ /// PERSISTED; read-only.
+ ///
public DateTime? IngestedAtUtc
{
get => _ingestedAtUtc;
@@ -47,67 +138,4 @@ public sealed record AuditLogRow
: null;
}
private readonly DateTime? _ingestedAtUtc;
-
- /// Trust-boundary channel the audited action crossed.
- public AuditChannel Channel { get; init; }
-
- /// Specific event kind within the channel.
- public AuditKind Kind { get; init; }
-
- /// Correlation id linking related audit rows (e.g. the cached-op lifecycle).
- public Guid? CorrelationId { get; init; }
-
- /// Id of the originating script execution / inbound request.
- public Guid? ExecutionId { get; init; }
-
- /// ExecutionId of the execution that spawned this run; null for top-level runs.
- public Guid? ParentExecutionId { get; init; }
-
- /// Site id where the action originated; null for central-direct events.
- public string? SourceSiteId { get; init; }
-
- /// The cluster node on which the event was emitted.
- public string? SourceNode { get; init; }
-
- /// Instance id where the action originated, when applicable.
- public string? SourceInstanceId { get; init; }
-
- /// Script that initiated the action, when applicable.
- public string? SourceScript { get; init; }
-
- /// Authenticated actor for inbound paths (API key name, user, etc.).
- public string? Actor { get; init; }
-
- /// Target of the action: external system name, db connection name, list name, or inbound method.
- public string? Target { get; init; }
-
- /// Lifecycle status of this row.
- public AuditStatus Status { get; init; }
-
- /// HTTP status code where applicable.
- public int? HttpStatus { get; init; }
-
- /// Duration of the audited action in milliseconds, when measurable.
- public int? DurationMs { get; init; }
-
- /// Human-readable error summary on failure rows.
- public string? ErrorMessage { get; init; }
-
- /// Verbose error detail (stack/exception) on failure rows.
- public string? ErrorDetail { get; init; }
-
- /// Truncated/redacted request summary; capped per AuditLogOptions.
- public string? RequestSummary { get; init; }
-
- /// Truncated/redacted response summary; capped per AuditLogOptions.
- public string? ResponseSummary { get; init; }
-
- /// True when Request/Response summaries were truncated to the payload cap.
- public bool PayloadTruncated { get; init; }
-
- /// Free-form JSON extension column for channel-specific extras.
- public string? Extra { get; init; }
-
- /// Site-local forwarding state; null on central rows.
- public AuditForwardState? ForwardState { get; init; }
}
diff --git a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/20260602174346_CollapseAuditLogToCanonical.Designer.cs b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/20260602174346_CollapseAuditLogToCanonical.Designer.cs
new file mode 100644
index 00000000..bc8aea5e
--- /dev/null
+++ b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/20260602174346_CollapseAuditLogToCanonical.Designer.cs
@@ -0,0 +1,1724 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase;
+
+#nullable disable
+
+namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Migrations
+{
+ [DbContext(typeof(ScadaBridgeDbContext))]
+ [Migration("20260602174346_CollapseAuditLogToCanonical")]
+ partial class CollapseAuditLogToCanonical
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "10.0.7")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("FriendlyName")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Xml")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("DataProtectionKeys");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.AuditLogEntry", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Action")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.Property("AfterStateJson")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("BundleImportId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("EntityId")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("EntityName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("EntityType")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("Timestamp")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("User")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Action");
+
+ b.HasIndex("BundleImportId")
+ .HasDatabaseName("IX_AuditLogEntries_BundleImportId");
+
+ b.HasIndex("EntityId");
+
+ b.HasIndex("EntityType");
+
+ b.HasIndex("Timestamp");
+
+ b.HasIndex("User");
+
+ b.ToTable("AuditLogEntries");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.SiteCall", b =>
+ {
+ b.Property("TrackedOperationId")
+ .HasMaxLength(36)
+ .IsUnicode(false)
+ .HasColumnType("varchar(36)");
+
+ b.Property("Channel")
+ .IsRequired()
+ .HasMaxLength(32)
+ .IsUnicode(false)
+ .HasColumnType("varchar(32)");
+
+ b.Property("CreatedAtUtc")
+ .HasColumnType("datetime2");
+
+ b.Property("HttpStatus")
+ .HasColumnType("int");
+
+ b.Property("IngestedAtUtc")
+ .HasColumnType("datetime2");
+
+ b.Property("LastError")
+ .HasMaxLength(1024)
+ .HasColumnType("nvarchar(1024)");
+
+ b.Property("RetryCount")
+ .HasColumnType("int");
+
+ b.Property("SourceNode")
+ .HasMaxLength(64)
+ .IsUnicode(false)
+ .HasColumnType("varchar(64)");
+
+ b.Property("SourceSite")
+ .IsRequired()
+ .HasMaxLength(64)
+ .IsUnicode(false)
+ .HasColumnType("varchar(64)");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(32)
+ .IsUnicode(false)
+ .HasColumnType("varchar(32)");
+
+ b.Property("Target")
+ .IsRequired()
+ .HasMaxLength(256)
+ .IsUnicode(false)
+ .HasColumnType("varchar(256)");
+
+ b.Property("TerminalAtUtc")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedAtUtc")
+ .HasColumnType("datetime2");
+
+ b.HasKey("TrackedOperationId");
+
+ b.HasIndex("SourceSite", "CreatedAtUtc")
+ .IsDescending(false, true)
+ .HasDatabaseName("IX_SiteCalls_Source_Created");
+
+ b.HasIndex("Status", "UpdatedAtUtc")
+ .IsDescending(false, true)
+ .HasDatabaseName("IX_SiteCalls_Status_Updated");
+
+ b.ToTable("SiteCalls", (string)null);
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Deployment.DeployedConfigSnapshot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ConfigurationJson")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DeployedAt")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("DeploymentId")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.Property("InstanceId")
+ .HasColumnType("int");
+
+ b.Property("RevisionHash")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DeploymentId");
+
+ b.HasIndex("InstanceId")
+ .IsUnique();
+
+ b.ToTable("DeployedConfigSnapshots");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Deployment.DeploymentRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CompletedAt")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("DeployedAt")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("DeployedBy")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("DeploymentId")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.Property("ErrorMessage")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("InstanceId")
+ .HasColumnType("int");
+
+ b.Property("RevisionHash")
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .IsRequired()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("rowversion");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("nvarchar(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DeployedAt");
+
+ b.HasIndex("DeploymentId")
+ .IsUnique();
+
+ b.HasIndex("InstanceId");
+
+ b.ToTable("DeploymentRecords");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Deployment.SystemArtifactDeploymentRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ArtifactType")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.Property("DeployedAt")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("DeployedBy")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("PerSiteStatus")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DeployedAt");
+
+ b.ToTable("SystemArtifactDeploymentRecords");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.ExternalSystems.DatabaseConnectionDefinition", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ConnectionString")
+ .IsRequired()
+ .HasMaxLength(8000)
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("MaxRetries")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("RetryDelay")
+ .HasColumnType("time");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("DatabaseConnectionDefinitions");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.ExternalSystems.ExternalSystemDefinition", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AuthConfiguration")
+ .HasMaxLength(8000)
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("AuthType")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("nvarchar(50)");
+
+ b.Property("EndpointUrl")
+ .IsRequired()
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.Property("MaxRetries")
+ .HasColumnType("int");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("RetryDelay")
+ .HasColumnType("time");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("ExternalSystemDefinitions");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.ExternalSystems.ExternalSystemMethod", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ExternalSystemDefinitionId")
+ .HasColumnType("int");
+
+ b.Property("HttpMethod")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("nvarchar(10)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("ParameterDefinitions")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.Property("ReturnDefinition")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ExternalSystemDefinitionId", "Name")
+ .IsUnique();
+
+ b.ToTable("ExternalSystemMethods");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi.ApiMethod", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("ParameterDefinitions")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.Property("ReturnDefinition")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.Property("Script")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TimeoutSeconds")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("ApiMethods");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances.Area", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("ParentAreaId")
+ .HasColumnType("int");
+
+ b.Property("SiteId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentAreaId");
+
+ b.HasIndex("SiteId", "ParentAreaId", "Name")
+ .IsUnique()
+ .HasFilter("[ParentAreaId] IS NOT NULL");
+
+ b.ToTable("Areas");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances.Instance", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AreaId")
+ .HasColumnType("int");
+
+ b.Property("SiteId")
+ .HasColumnType("int");
+
+ b.Property("State")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("nvarchar(50)");
+
+ b.Property("TemplateId")
+ .HasColumnType("int");
+
+ b.Property("UniqueName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AreaId");
+
+ b.HasIndex("TemplateId");
+
+ b.HasIndex("SiteId", "UniqueName")
+ .IsUnique();
+
+ b.ToTable("Instances");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances.InstanceAlarmOverride", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AlarmCanonicalName")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("InstanceId")
+ .HasColumnType("int");
+
+ b.Property("PriorityLevelOverride")
+ .HasColumnType("int");
+
+ b.Property("TriggerConfigurationOverride")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId", "AlarmCanonicalName")
+ .IsUnique();
+
+ b.ToTable("InstanceAlarmOverrides");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances.InstanceAttributeOverride", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AttributeName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("InstanceId")
+ .HasColumnType("int");
+
+ b.Property("OverrideValue")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId", "AttributeName")
+ .IsUnique();
+
+ b.ToTable("InstanceAttributeOverrides");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances.InstanceConnectionBinding", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AttributeName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("DataConnectionId")
+ .HasColumnType("int");
+
+ b.Property("DataSourceReferenceOverride")
+ .HasMaxLength(512)
+ .HasColumnType("nvarchar(512)");
+
+ b.Property("InstanceId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DataConnectionId");
+
+ b.HasIndex("InstanceId", "AttributeName")
+ .IsUnique();
+
+ b.ToTable("InstanceConnectionBindings");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances.InstanceNativeAlarmSourceOverride", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ConditionFilterOverride")
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property("ConnectionNameOverride")
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("InstanceId")
+ .HasColumnType("int");
+
+ b.Property("SourceCanonicalName")
+ .IsRequired()
+ .HasMaxLength(400)
+ .HasColumnType("nvarchar(400)");
+
+ b.Property("SourceReferenceOverride")
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InstanceId", "SourceCanonicalName")
+ .IsUnique();
+
+ b.ToTable("InstanceNativeAlarmSourceOverrides");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications.Notification", b =>
+ {
+ b.Property("NotificationId")
+ .HasMaxLength(64)
+ .HasColumnType("nvarchar(64)");
+
+ b.Property("Body")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("DeliveredAt")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("LastAttemptAt")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("LastError")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.Property("ListName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("NextAttemptAt")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("OriginExecutionId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("OriginParentExecutionId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("ResolvedTargets")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("RetryCount")
+ .HasColumnType("int");
+
+ b.Property("SiteEnqueuedAt")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("SourceInstanceId")
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("SourceNode")
+ .HasMaxLength(64)
+ .IsUnicode(false)
+ .HasColumnType("varchar(64)");
+
+ b.Property("SourceScript")
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("SourceSiteId")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("nvarchar(32)");
+
+ b.Property("Subject")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("nvarchar(32)");
+
+ b.Property("TypeData")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("NotificationId");
+
+ b.HasIndex("SourceSiteId", "CreatedAt");
+
+ b.HasIndex("Status", "NextAttemptAt");
+
+ b.ToTable("Notifications");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications.NotificationList", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("nvarchar(32)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("NotificationLists");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications.NotificationRecipient", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("EmailAddress")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("NotificationListId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NotificationListId");
+
+ b.ToTable("NotificationRecipients");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications.SmtpConfiguration", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AuthType")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("nvarchar(50)");
+
+ b.Property("ConnectionTimeoutSeconds")
+ .HasColumnType("int");
+
+ b.Property("Credentials")
+ .HasMaxLength(8000)
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("FromAddress")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("Host")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("MaxConcurrentConnections")
+ .HasColumnType("int");
+
+ b.Property("MaxRetries")
+ .HasColumnType("int");
+
+ b.Property("Port")
+ .HasColumnType("int");
+
+ b.Property("RetryDelay")
+ .HasColumnType("time");
+
+ b.Property("TlsMode")
+ .HasMaxLength(50)
+ .HasColumnType("nvarchar(50)");
+
+ b.HasKey("Id");
+
+ b.ToTable("SmtpConfigurations");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Scripts.SharedScript", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Code")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("ParameterDefinitions")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.Property("ReturnDefinition")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("SharedScripts");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Security.LdapGroupMapping", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("LdapGroupName")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("Role")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("LdapGroupName")
+ .IsUnique();
+
+ b.ToTable("LdapGroupMappings");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ LdapGroupName = "SCADA-Admins",
+ Role = "Administrator"
+ },
+ new
+ {
+ Id = 2,
+ LdapGroupName = "SCADA-Designers",
+ Role = "Designer"
+ },
+ new
+ {
+ Id = 3,
+ LdapGroupName = "SCADA-Deploy-All",
+ Role = "Deployer"
+ },
+ new
+ {
+ Id = 4,
+ LdapGroupName = "SCADA-Deploy-SiteA",
+ Role = "Deployer"
+ });
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Security.SiteScopeRule", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("LdapGroupMappingId")
+ .HasColumnType("int");
+
+ b.Property("SiteId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SiteId");
+
+ b.HasIndex("LdapGroupMappingId", "SiteId")
+ .IsUnique();
+
+ b.ToTable("SiteScopeRules");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites.DataConnection", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("BackupConfiguration")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.Property("FailoverRetryCount")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasDefaultValue(3);
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("PrimaryConfiguration")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.Property("Protocol")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("nvarchar(50)");
+
+ b.Property("SiteId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SiteId", "Name")
+ .IsUnique();
+
+ b.ToTable("DataConnections");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites.Site", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Description")
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.Property("GrpcNodeAAddress")
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("GrpcNodeBAddress")
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("NodeAAddress")
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("NodeBAddress")
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("SiteIdentifier")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("nvarchar(100)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.HasIndex("SiteIdentifier")
+ .IsUnique();
+
+ b.ToTable("Sites");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates.Template", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Description")
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.Property("FolderId")
+ .HasColumnType("int");
+
+ b.Property("IsDerived")
+ .HasColumnType("bit");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("OwnerCompositionId")
+ .HasColumnType("int");
+
+ b.Property("ParentTemplateId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FolderId");
+
+ b.HasIndex("Name")
+ .IsUnique()
+ .HasFilter("[IsDerived] = 0");
+
+ b.HasIndex("ParentTemplateId");
+
+ b.ToTable("Templates");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates.TemplateAlarm", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Description")
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.Property("IsInherited")
+ .HasColumnType("bit");
+
+ b.Property("IsLocked")
+ .HasColumnType("bit");
+
+ b.Property("LockedInDerived")
+ .HasColumnType("bit");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("OnTriggerScriptId")
+ .HasColumnType("int");
+
+ b.Property("PriorityLevel")
+ .HasColumnType("int");
+
+ b.Property("TemplateId")
+ .HasColumnType("int");
+
+ b.Property("TriggerConfiguration")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.Property("TriggerType")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("nvarchar(50)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TemplateId", "Name")
+ .IsUnique();
+
+ b.ToTable("TemplateAlarms");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates.TemplateAttribute", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("DataSourceReference")
+ .HasMaxLength(500)
+ .HasColumnType("nvarchar(500)");
+
+ b.Property("DataType")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("nvarchar(50)");
+
+ b.Property("Description")
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.Property("IsInherited")
+ .HasColumnType("bit");
+
+ b.Property("IsLocked")
+ .HasColumnType("bit");
+
+ b.Property("LockedInDerived")
+ .HasColumnType("bit");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("TemplateId")
+ .HasColumnType("int");
+
+ b.Property("Value")
+ .HasMaxLength(4000)
+ .HasColumnType("nvarchar(4000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TemplateId", "Name")
+ .IsUnique();
+
+ b.ToTable("TemplateAttributes");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates.TemplateComposition", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ComposedTemplateId")
+ .HasColumnType("int");
+
+ b.Property("InstanceName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("TemplateId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ComposedTemplateId");
+
+ b.HasIndex("TemplateId", "InstanceName")
+ .IsUnique();
+
+ b.ToTable("TemplateCompositions");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates.TemplateFolder", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("ParentFolderId")
+ .HasColumnType("int");
+
+ b.Property("SortOrder")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentFolderId", "Name")
+ .IsUnique()
+ .HasFilter("[ParentFolderId] IS NOT NULL");
+
+ b.ToTable("TemplateFolders");
+ });
+
+ modelBuilder.Entity("ZB.MOM.WW.ScadaBridge.Commons.Entities.Templates.TemplateNativeAlarmSource", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ConditionFilter")
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property("ConnectionName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("Description")
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.Property("IsInherited")
+ .HasColumnType("bit");
+
+ b.Property("IsLocked")
+ .HasColumnType("bit");
+
+ b.Property("LockedInDerived")
+ .HasColumnType("bit");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("nvarchar(200)");
+
+ b.Property("SourceReference")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("nvarchar(1000)");
+
+ b.Property