diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0de866b6..0d54c18a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,53 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
+### Changed — BREAKING: canonical role names + audit separation-of-duties collapse (Task 1.7)
+
+Role string VALUES are standardized onto the canonical vocabulary
+(`Administrator`/`Designer`/`Deployer`/`Viewer`; `Operator`/`Engineer` are unused
+by ScadaBridge). The legacy ScadaBridge role names were renamed and two were
+**collapsed**:
+
+| Legacy role | Canonical role | Notes |
+|-----------------|-----------------|-------|
+| `Admin` | `Administrator` | rename |
+| `Design` | `Designer` | rename |
+| `Deployment` | `Deployer` | rename |
+| `Audit` | `Administrator` | **COLLAPSE** |
+| `AuditReadOnly` | `Viewer` | **COLLAPSE** |
+
+- **SECURITY — privilege escalation (accepted).** The former `Audit` role
+ collapses into `Administrator`. This is a real escalation: a former audit-only
+ user now holds the **entire admin surface** (create/update/delete sites, manage
+ LDAP group→role mappings and API keys, preview/import transport bundles), not
+ just audit read+export. This loss of auditor/admin separation-of-duties is a
+ deliberate, accepted trade-off of the canonicalization.
+- **SECURITY — half-SoD preserved.** The former `AuditReadOnly` role collapses
+ into `Viewer`, which **keeps audit READ** (Audit Log page, Configuration Audit
+ Log page, audit nav group) but **cannot bulk-export**. The audit policy sets are
+ now `OperationalAuditRoles = { Administrator, Viewer }` and
+ `AuditExportRoles = { Administrator }`, so a `Viewer` reads the audit log but the
+ Export-CSV button / `/api/audit/export` endpoint correctly refuses it.
+- **Enforcement.** Every enforcement site moved together: the role-claim values,
+ the authorization policies (`RequireAdmin`/`RequireDesign`/`RequireDeployment`
+ policy *names* are unchanged; only the role *values* inside them changed), the
+ `ManagementActor.GetRequiredRole` switch, the hard-coded site-scope admin-bypass
+ (`Roles.Administrator` everywhere), the `DebugStreamHub` Administrator/Deployer
+ gates, and the CentralUI `BrowseService`/`BindingTester` Designer guards.
+ **Site-scoping logic is otherwise unchanged** — only the admin-bypass *value*
+ moved from `"Admin"` to `Roles.Administrator`.
+- **Config-DB migration `CanonicalizeRoles`.** Updates the four seeded
+ `LdapGroupMappings` rows (Id 1-4) to the canonical role values and adds raw
+ idempotent catch-all `UPDATE`s for operator-added rows
+ (`Admin`/`Audit`→`Administrator`, `Design`→`Designer`, `Deployment`→`Deployer`,
+ `AuditReadOnly`→`Viewer`). The Down migration is **lossy** for the collapse: it
+ best-effort maps `Administrator`→`Admin` and `Viewer`→`AuditReadOnly` but cannot
+ recover the original `Audit`/`Admin` or `Viewer`/`AuditReadOnly` distinction.
+- **Operator action.** Any LDAP group→role mappings created with the legacy role
+ strings are migrated automatically by `CanonicalizeRoles`. New mappings created
+ via the CentralUI LDAP-mappings form now offer the canonical role values
+ (including a `Viewer` option for audit-read-only delegation).
+
### Changed — BREAKING: inbound API authentication
Inbound API authentication has migrated off the SQL Server `X-API-Key` scheme and
diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Admin/LdapMappingForm.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Admin/LdapMappingForm.razor
index 758040da..47c9b76e 100644
--- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Admin/LdapMappingForm.razor
+++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Admin/LdapMappingForm.razor
@@ -30,11 +30,12 @@
Role
Select role...
- Admin
- Design
- Deployment
+ Administrator
+ Designer
+ Deployer
+ Viewer
-
Deployment role: configure site scope below after saving.
+ Deployer role: configure site scope below after saving.
@if (_formError != null)
{
diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BindingTester.cs b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BindingTester.cs
index e8e1c7d5..8f9d6b3c 100644
--- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BindingTester.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BindingTester.cs
@@ -36,11 +36,11 @@ public sealed class BindingTester : IBindingTester
CancellationToken ct = default)
{
// CentralUI-side role guard — sites don't enforce envelope-level
- // roles, so the Design check must happen here before any cross-cluster
+ // roles, so the Designer check must happen here before any cross-cluster
// traffic. Use HasClaim against JwtTokenService.RoleClaimType (not
// IsInRole, per c1e16cf).
var state = await _auth.GetAuthenticationStateAsync();
- if (!state.User.HasClaim(JwtTokenService.RoleClaimType, "Design"))
+ if (!state.User.HasClaim(JwtTokenService.RoleClaimType, Roles.Designer))
{
return new ReadTagValuesResult(
Array.Empty(),
diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BrowseService.cs b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BrowseService.cs
index b27f4298..968df7f6 100644
--- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BrowseService.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Services/BrowseService.cs
@@ -43,9 +43,9 @@ public sealed class BrowseService : IBrowseService
CancellationToken cancellationToken = default)
{
// CentralUI-side role guard — sites don't enforce envelope-level roles,
- // so the Design check must happen here before any cross-cluster traffic.
+ // so the Designer check must happen here before any cross-cluster traffic.
var state = await _auth.GetAuthenticationStateAsync();
- if (!state.User.HasClaim(JwtTokenService.RoleClaimType, "Design"))
+ if (!state.User.HasClaim(JwtTokenService.RoleClaimType, Roles.Designer))
{
return new BrowseNodeResult(
Array.Empty(),
diff --git a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Configurations/SecurityConfiguration.cs b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Configurations/SecurityConfiguration.cs
index 912d268e..109b5142 100644
--- a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Configurations/SecurityConfiguration.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Configurations/SecurityConfiguration.cs
@@ -25,12 +25,15 @@ public class LdapGroupMappingConfiguration : IEntityTypeConfiguration m.LdapGroupName).IsUnique();
- // Seed default group mappings matching GLAuth test users
+ // Seed default group mappings matching GLAuth test users.
+ // Role VALUES are the canonical six (Task 1.7): Administrator/Designer/
+ // Deployer. The LDAP group NAMES (SCADA-Admins etc.) are unchanged —
+ // only the role each group maps to was canonicalized.
builder.HasData(
- new LdapGroupMapping("SCADA-Admins", "Admin") { Id = 1 },
- new LdapGroupMapping("SCADA-Designers", "Design") { Id = 2 },
- new LdapGroupMapping("SCADA-Deploy-All", "Deployment") { Id = 3 },
- new LdapGroupMapping("SCADA-Deploy-SiteA", "Deployment") { Id = 4 });
+ new LdapGroupMapping("SCADA-Admins", "Administrator") { Id = 1 },
+ new LdapGroupMapping("SCADA-Designers", "Designer") { Id = 2 },
+ new LdapGroupMapping("SCADA-Deploy-All", "Deployer") { Id = 3 },
+ new LdapGroupMapping("SCADA-Deploy-SiteA", "Deployer") { Id = 4 });
}
}
diff --git a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/20260602113822_CanonicalizeRoles.Designer.cs b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/20260602113822_CanonicalizeRoles.Designer.cs
new file mode 100644
index 00000000..b013f343
--- /dev/null
+++ b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/20260602113822_CanonicalizeRoles.Designer.cs
@@ -0,0 +1,1740 @@
+//
+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("20260602113822_CanonicalizeRoles")]
+ partial class CanonicalizeRoles
+ {
+ ///
+ 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.AuditEvent", b =>
+ {
+ b.Property("EventId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("OccurredAtUtc")
+ .HasColumnType("datetime2");
+
+ b.Property("Actor")
+ .HasMaxLength(128)
+ .IsUnicode(false)
+ .HasColumnType("varchar(128)");
+
+ b.Property("Channel")
+ .IsRequired()
+ .HasMaxLength(32)
+ .IsUnicode(false)
+ .HasColumnType("varchar(32)");
+
+ b.Property("CorrelationId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("DurationMs")
+ .HasColumnType("int");
+
+ b.Property("ErrorDetail")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ErrorMessage")
+ .HasMaxLength(1024)
+ .HasColumnType("nvarchar(1024)");
+
+ b.Property("ExecutionId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("Extra")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ForwardState")
+ .HasMaxLength(32)
+ .IsUnicode(false)
+ .HasColumnType("varchar(32)");
+
+ b.Property("HttpStatus")
+ .HasColumnType("int");
+
+ b.Property("IngestedAtUtc")
+ .HasColumnType("datetime2");
+
+ b.Property("Kind")
+ .IsRequired()
+ .HasMaxLength(32)
+ .IsUnicode(false)
+ .HasColumnType("varchar(32)");
+
+ b.Property("ParentExecutionId")
+ .HasColumnType("uniqueidentifier");
+
+ b.Property("PayloadTruncated")
+ .HasColumnType("bit");
+
+ b.Property("RequestSummary")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ResponseSummary")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("SourceInstanceId")
+ .HasMaxLength(128)
+ .IsUnicode(false)
+ .HasColumnType("varchar(128)");
+
+ b.Property("SourceNode")
+ .HasMaxLength(64)
+ .IsUnicode(false)
+ .HasColumnType("varchar(64)");
+
+ b.Property("SourceScript")
+ .HasMaxLength(128)
+ .IsUnicode(false)
+ .HasColumnType("varchar(128)");
+
+ b.Property("SourceSiteId")
+ .HasMaxLength(64)
+ .IsUnicode(false)
+ .HasColumnType("varchar(64)");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasMaxLength(32)
+ .IsUnicode(false)
+ .HasColumnType("varchar(32)");
+
+ b.Property("Target")
+ .HasMaxLength(256)
+ .IsUnicode(false)
+ .HasColumnType("varchar(256)");
+
+ b.HasKey("EventId", "OccurredAtUtc");
+
+ b.HasIndex("CorrelationId")
+ .HasDatabaseName("IX_AuditLog_CorrelationId")
+ .HasFilter("[CorrelationId] IS NOT NULL");
+
+ b.HasIndex("EventId")
+ .IsUnique()
+ .HasDatabaseName("UX_AuditLog_EventId");
+
+ b.HasIndex("ExecutionId")
+ .HasDatabaseName("IX_AuditLog_Execution")
+ .HasFilter("[ExecutionId] IS NOT NULL");
+
+ b.HasIndex("OccurredAtUtc")
+ .IsDescending()
+ .HasDatabaseName("IX_AuditLog_OccurredAtUtc");
+
+ b.HasIndex("ParentExecutionId")
+ .HasDatabaseName("IX_AuditLog_ParentExecution")
+ .HasFilter("[ParentExecutionId] IS NOT NULL");
+
+ b.HasIndex("SourceNode", "OccurredAtUtc")
+ .HasDatabaseName("IX_AuditLog_Node_Occurred");
+
+ b.HasIndex("SourceSiteId", "OccurredAtUtc")
+ .IsDescending(false, true)
+ .HasDatabaseName("IX_AuditLog_Site_Occurred");
+
+ b.HasIndex("Target", "OccurredAtUtc")
+ .IsDescending(false, true)
+ .HasDatabaseName("IX_AuditLog_Target_Occurred")
+ .HasFilter("[Target] IS NOT NULL");
+
+ b.HasIndex("Channel", "Status", "OccurredAtUtc")
+ .IsDescending(false, false, true)
+ .HasDatabaseName("IX_AuditLog_Channel_Status_Occurred");
+
+ b.ToTable("AuditLog", (string)null);
+ });
+
+ 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