docs(comments): strip internal task/milestone/bundle bookkeeping from code comments
Remove project bookkeeping citations from shipped code comments across the solution: hyphenated task IDs (WP-14, StoreAndForward-025), milestone/task/ issue refs (M3, Task 4, Audit Log #23, #21), Bundle X task-bundle labels, and C/D/K/S/T phase labels. Comment text only — no code logic, string/log literals, or XML-doc structure changed. Genuine descriptions are preserved (only the citation is stripped), and technical lookalikes are retained (UTF-8, SHA-256, T00:00:00, M365, UTC-5, pre-C4/pre-C5 schema versions). Flagged by the new CommentChecker TaskReferenceInComment / TrackingReferenceInComment checks plus targeted grep passes; full solution builds clean, append-only guard tests pass.
This commit is contained in:
@@ -11,7 +11,7 @@ using ZB.MOM.WW.ScadaBridge.Security;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Minimal-API endpoint hosting the Audit Log CSV export (#23 M7-T14 / Bundle F).
|
||||
/// Minimal-API endpoint hosting the Audit Log CSV export.
|
||||
///
|
||||
/// <para>
|
||||
/// CentralUI ships no MVC controllers (see <see cref="ZB.MOM.WW.ScadaBridge.CentralUI.Auth.AuthEndpoints"/>
|
||||
@@ -24,7 +24,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Audit;
|
||||
///
|
||||
/// <para>
|
||||
/// The route is gated on the <see cref="AuthorizationPolicies.AuditExport"/>
|
||||
/// policy (#23 M7-T15 / Bundle G) so only roles with the bulk-export
|
||||
/// policy so only roles with the bulk-export
|
||||
/// permission can pull a CSV — the page-level
|
||||
/// <see cref="AuthorizationPolicies.OperationalAudit"/> gate is read-only
|
||||
/// and intentionally narrower. The query-string parser silently drops
|
||||
|
||||
@@ -45,7 +45,7 @@ public static class AuthEndpoints
|
||||
}
|
||||
|
||||
// Map LDAP groups to roles via the shared IGroupRoleMapper<string> seam
|
||||
// (Task 1.1 ScadaBridgeGroupRoleMapper, wrapping the DB-backed RoleMapper).
|
||||
// (ScadaBridgeGroupRoleMapper, wrapping the DB-backed RoleMapper).
|
||||
// The full RoleMappingResult — including PermittedSiteIds and the
|
||||
// system-wide flag — is carried in the mapping's opaque Scope so the
|
||||
// site-scope→SiteId claims below are built exactly as before.
|
||||
@@ -63,7 +63,7 @@ public static class AuthEndpoints
|
||||
: new RoleMappingResult(roleMapping.Roles, [], IsSystemWideDeployment: false);
|
||||
|
||||
// Build claims from LDAP auth + role mapping.
|
||||
// CentralUI-005: no fixed "expires_at" absolute-cap claim is stamped
|
||||
// No fixed "expires_at" absolute-cap claim is stamped
|
||||
// — session expiry is owned by the cookie middleware's sliding window
|
||||
// (ZB.MOM.WW.ScadaBridge.Security AddCookie: ExpireTimeSpan = idle timeout,
|
||||
// SlidingExpiration = true). A frozen absolute claim would contradict
|
||||
@@ -71,11 +71,11 @@ public static class AuthEndpoints
|
||||
var displayName = string.IsNullOrEmpty(authResult.DisplayName) ? username : authResult.DisplayName;
|
||||
var resolvedUsername = string.IsNullOrEmpty(authResult.Username) ? username : authResult.Username;
|
||||
|
||||
// M2.19 (#15): build the cookie principal through the shared
|
||||
// Build the cookie principal through the shared
|
||||
// SessionClaimBuilder — the SINGLE source of truth that the mid-session
|
||||
// OnValidatePrincipal role-refresh path ALSO uses, so login and refresh can
|
||||
// never drift. It stamps the canonical identity/role/scope claims (with
|
||||
// roleType/nameType pinned for IsInRole), PLUS the M2.19 additions: one
|
||||
// roleType/nameType pinned for IsInRole), PLUS these additions: one
|
||||
// zb:group claim per raw LDAP group (the durable input the mid-session
|
||||
// RoleMapper re-run consumes) and a zb:lastrolerefresh anchor (login time,
|
||||
// UTC) that also seeds the LastActivity idle anchor. The refresh timestamp is
|
||||
@@ -147,7 +147,7 @@ public static class AuthEndpoints
|
||||
});
|
||||
}).DisableAntiforgery();
|
||||
|
||||
// Logout is a state-changing authenticated action (CentralUI-017): it
|
||||
// Logout is a state-changing authenticated action: it
|
||||
// keeps antiforgery validation enabled so it cannot be triggered
|
||||
// cross-site. The NavMenu sign-out form includes the antiforgery token
|
||||
// (rendered by the <AntiforgeryToken /> component). There is deliberately
|
||||
@@ -159,9 +159,9 @@ public static class AuthEndpoints
|
||||
context.Response.Redirect("/login");
|
||||
});
|
||||
|
||||
// CentralUI-020: liveness probe for the client-side idle-logout check.
|
||||
// Liveness probe for the client-side idle-logout check.
|
||||
// The Blazor circuit's CookieAuthenticationStateProvider serves a frozen
|
||||
// constructor-time principal (CentralUI-004), so a circuit can never
|
||||
// constructor-time principal, so a circuit can never
|
||||
// observe a server-side cookie expiry by polling the auth state.
|
||||
// SessionExpiry instead polls this endpoint via fetch(): being a normal
|
||||
// HTTP request, the cookie middleware re-validates (and slides) the
|
||||
@@ -178,7 +178,7 @@ public static class AuthEndpoints
|
||||
/// <summary>
|
||||
/// Handler for <c>GET /auth/ping</c>. Returns <c>200</c> while the caller's
|
||||
/// cookie session is still valid and <c>401</c> once it has lapsed
|
||||
/// server-side. See CentralUI-020.
|
||||
/// server-side.
|
||||
/// </summary>
|
||||
/// <param name="context">The current HTTP context used to check authentication state and write the response.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
@@ -192,7 +192,7 @@ public static class AuthEndpoints
|
||||
|
||||
/// <summary>
|
||||
/// Builds the <see cref="AuthenticationProperties"/> for the login sign-in.
|
||||
/// CentralUI-005: deliberately does <b>not</b> set <see cref="AuthenticationProperties.ExpiresUtc"/>.
|
||||
/// Deliberately does <b>not</b> set <see cref="AuthenticationProperties.ExpiresUtc"/>.
|
||||
/// Session expiry is owned by the cookie authentication middleware's sliding
|
||||
/// window (configured in <c>ZB.MOM.WW.ScadaBridge.Security</c>'s <c>AddCookie</c>:
|
||||
/// <c>ExpireTimeSpan</c> = the idle timeout, <c>SlidingExpiration = true</c>).
|
||||
|
||||
@@ -5,7 +5,7 @@ using ZB.MOM.WW.ScadaBridge.Security;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
|
||||
|
||||
/// <summary>
|
||||
/// Claim-lookup helpers for the Central UI. CentralUI-024: claim types are owned
|
||||
/// Claim-lookup helpers for the Central UI. Claim types are owned
|
||||
/// by <see cref="JwtTokenService"/> (the single source of truth). These helpers
|
||||
/// resolve them through the <c>JwtTokenService</c> constants so a rename there
|
||||
/// propagates here instead of silently breaking ten copy-pasted call sites.
|
||||
@@ -36,7 +36,7 @@ public static class ClaimsPrincipalExtensions
|
||||
/// <summary>
|
||||
/// Resolves the current user's audit username from the auth state provider.
|
||||
/// Replaces the <c>GetCurrentUserAsync</c> helper that was copy-pasted into
|
||||
/// ten components (CentralUI-024).
|
||||
/// ten components.
|
||||
/// </summary>
|
||||
/// <param name="authStateProvider">The Blazor authentication state provider to read from.</param>
|
||||
/// <returns>A task that resolves to the current user's audit username, or <see cref="UnknownUser"/> if not authenticated.</returns>
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// We must NOT read <see cref="IHttpContextAccessor"/> on every
|
||||
/// <see cref="GetAuthenticationStateAsync"/> call (CentralUI-004): for the
|
||||
/// <see cref="GetAuthenticationStateAsync"/> call: for the
|
||||
/// lifetime of a long-lived SignalR circuit <c>HttpContext</c> is <c>null</c>
|
||||
/// (or, worse, a stale/foreign context), so a later re-evaluation —
|
||||
/// e.g. <c><AuthorizeView></c> re-rendering — would otherwise see an
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the set of sites the current user is permitted to operate on, from
|
||||
/// the <c>SiteId</c> claims attached at login (CentralUI-002).
|
||||
/// the <c>SiteId</c> claims attached at login.
|
||||
/// <para>
|
||||
/// The design (Component-CentralUI, CLAUDE.md "Security & Auth") makes the
|
||||
/// Deployment role site-scoped: a Deployment user mapped through an LDAP group
|
||||
@@ -93,7 +93,7 @@ public sealed class SiteScopeService
|
||||
}
|
||||
|
||||
// No SiteId claims => system-wide. Absence of scope rules means an
|
||||
// unrestricted deployer (Security-017 made this service the sole
|
||||
// unrestricted deployer (this service is the sole
|
||||
// site-scoping mechanism — there is no separate handler to mirror).
|
||||
var result = (IsSystemWide: ids.Count == 0, Sites: (IReadOnlySet<int>)ids);
|
||||
_cached = result;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using ZB.MOM.WW.ScadaBridge.CentralUI.Services
|
||||
@using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums
|
||||
|
||||
@* Audit Log drilldown drawer (#23 M7 Bundle C / M7-T4..T8).
|
||||
@* Audit Log drilldown drawer.
|
||||
Right-side Bootstrap offcanvas-style drawer hosted by the Audit Log page.
|
||||
The drawer owns only the offcanvas chrome (backdrop, header, Close buttons);
|
||||
the single-AuditEvent detail body is delegated to <AuditEventDetail>, which
|
||||
|
||||
@@ -4,7 +4,7 @@ using ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Child component for the central Audit Log page (#23 M7 Bundle C / M7-T4..T8).
|
||||
/// Child component for the central Audit Log page.
|
||||
/// Renders one <see cref="AuditEventView"/> in a right-side off-canvas drawer.
|
||||
/// The drawer owns only the offcanvas chrome — backdrop, header, and the two
|
||||
/// Close buttons; the single-row detail body (read-only fields, conditional
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using ZB.MOM.WW.ScadaBridge.CentralUI.Services
|
||||
@using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums
|
||||
|
||||
@* Reusable single-AuditEvent detail body (#23 M7 Bundle C / M7-T4..T8).
|
||||
@* Reusable single-AuditEvent detail body.
|
||||
Extracted from AuditDrilldownDrawer so the drawer and the execution-tree
|
||||
node-detail modal share one rendering of a row's detail.
|
||||
All form/field rendering follows the form-layout memory:
|
||||
|
||||
@@ -9,8 +9,8 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Reusable single-<see cref="AuditEvent"/> detail body (#23 M7 Bundle C /
|
||||
/// M7-T4..T8). Extracted verbatim from <see cref="AuditDrilldownDrawer"/> so
|
||||
/// Reusable single-<see cref="AuditEvent"/> detail body. Extracted verbatim
|
||||
/// from <see cref="AuditDrilldownDrawer"/> so
|
||||
/// the drawer and the execution-tree node-detail modal render a row's detail
|
||||
/// identically. Renders the read-only field list, the conditional
|
||||
/// Error/Request/Response/Extra subsections, and the action buttons (Copy as
|
||||
@@ -54,7 +54,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Audit;
|
||||
/// <see cref="AuditEvent.ParentExecutionId"/> is set the "View parent
|
||||
/// execution" button navigates to <c>/audit/log?executionId={parentId}</c>
|
||||
/// — the spawner's id used as the per-run drill-in target. All are deep
|
||||
/// links the Audit Log page deserializes on init (Bundle D) and auto-loads.
|
||||
/// links the Audit Log page deserializes on init and auto-loads.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public partial class AuditEventDetail
|
||||
@@ -281,7 +281,7 @@ public partial class AuditEventDetail
|
||||
|
||||
/// <summary>
|
||||
/// Drill-in to the execution-chain TREE view (Audit Log ParentExecutionId
|
||||
/// feature, Task 10). Navigates to
|
||||
/// feature). Navigates to
|
||||
/// <c>/audit/execution-tree?executionId={ExecutionId}</c> — the tree page
|
||||
/// resolves the whole chain rooted at the topmost ancestor and renders it
|
||||
/// expandably, with this row's execution highlighted. The button is only
|
||||
|
||||
@@ -6,7 +6,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Filter bar for the central Audit Log page (#23 M7-T2). Owns the
|
||||
/// Filter bar for the central Audit Log page. Owns the
|
||||
/// <see cref="AuditQueryModel"/> binding state and renders the filter controls
|
||||
/// — Channel as a single-select (one channel at a time, so the Kind options
|
||||
/// narrow to it cleanly); Kind / Status / Site as compact
|
||||
@@ -55,7 +55,7 @@ public partial class AuditFilterBar
|
||||
[Parameter] public Func<DateTime>? NowUtcProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Bundle D drill-in seam (#23 M7-T10..T12). When set on first render,
|
||||
/// When set on first render,
|
||||
/// pre-populates the Instance free-text input. Instance is UI-only — the
|
||||
/// repository filter contract has no instance column — so this flows in
|
||||
/// through a separate parameter rather than the <see cref="AuditLogQueryFilter"/>
|
||||
@@ -167,7 +167,7 @@ public partial class AuditFilterBar
|
||||
|
||||
private async Task Apply()
|
||||
{
|
||||
// CentralUI-026: <input type="datetime-local"> binds with DateTimeKind.Unspecified
|
||||
// <input type="datetime-local"> binds with DateTimeKind.Unspecified
|
||||
// — the value is the user's browser-local wall-clock. Tag it as Local then convert
|
||||
// to UTC before the model emits the filter, otherwise a non-UTC operator's window
|
||||
// is silently shifted by their UTC offset. Done on a swap-and-restore basis so the
|
||||
|
||||
@@ -5,7 +5,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// UI-side binding model for <see cref="AuditFilterBar"/> (#23 M7-T2).
|
||||
/// UI-side binding model for <see cref="AuditFilterBar"/>.
|
||||
///
|
||||
/// <para>
|
||||
/// The model mirrors <see cref="AuditLogQueryFilter"/> but allows multi-select chip
|
||||
|
||||
@@ -8,7 +8,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Keyset-paged results grid for the central Audit Log page (#23 M7-T3).
|
||||
/// Keyset-paged results grid for the central Audit Log page.
|
||||
/// Renders the columns named in Component-AuditLog.md §10 — OccurredAtUtc,
|
||||
/// Site, Channel, Kind, Status, Target, Actor, DurationMs, HttpStatus,
|
||||
/// ErrorMessage — plus the ExecutionId per-run correlation column and the
|
||||
@@ -66,7 +66,7 @@ public partial class AuditResultsGrid : IAsyncDisposable
|
||||
private bool _loading;
|
||||
private string? _error;
|
||||
|
||||
// CentralUI-032: small in-component stack of prior-page cursors so the user
|
||||
// Small in-component stack of prior-page cursors so the user
|
||||
// can step backwards through results. Each Next push captures the cursor
|
||||
// that produced the current page (null for page 1) before advancing; each
|
||||
// Previous pop reloads the page at the recovered cursor. Mirrors the
|
||||
@@ -108,7 +108,7 @@ public partial class AuditResultsGrid : IAsyncDisposable
|
||||
[Parameter] public IReadOnlyList<string>? ColumnOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the user clicks a row. Bundle C wires this to the drilldown
|
||||
/// Raised when the user clicks a row. Consumers wire this to the drilldown
|
||||
/// drawer. The event payload is the full <see cref="AuditEventView"/>.
|
||||
/// </summary>
|
||||
[Parameter] public EventCallback<AuditEventView> OnRowSelected { get; set; }
|
||||
@@ -228,15 +228,15 @@ public partial class AuditResultsGrid : IAsyncDisposable
|
||||
AfterOccurredAtUtc: last.OccurredAtUtc,
|
||||
AfterEventId: last.EventId);
|
||||
|
||||
// CentralUI-032: remember the cursor that produced the current page so
|
||||
// a later Previous can navigate back to it. The page-1 entry is pushed
|
||||
// Remember the cursor that produced the current page so
|
||||
// a later Previous can navigate back to it. The first-page entry is pushed
|
||||
// as null — LoadAsync treats null as "first page" (PageSize-only).
|
||||
_cursorStack.Push(_currentPaging);
|
||||
await LoadAsync(cursor);
|
||||
_pageNumber++;
|
||||
}
|
||||
|
||||
// CentralUI-032: pops the previous-page cursor off the stack and reloads
|
||||
// Pops the previous-page cursor off the stack and reloads
|
||||
// at that position. The pop only happens AFTER a successful reload — a
|
||||
// failed page-fetch leaves the user on the current page with the error
|
||||
// banner instead of stranding them between pages.
|
||||
|
||||
@@ -8,8 +8,8 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Execution-Tree Node Detail Modal (Execution-Tree Node Detail Modal feature,
|
||||
/// Task 3). Opened from an execution-tree node double-click: given an
|
||||
/// Execution-Tree Node Detail Modal. Opened from an execution-tree node
|
||||
/// double-click: given an
|
||||
/// <see cref="ExecutionId"/> it loads that execution's audit rows via
|
||||
/// <see cref="IAuditLogQueryService"/> and shows a list → per-row detail.
|
||||
///
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Recursive Blazor tree component for the execution-chain view (Audit Log
|
||||
/// ParentExecutionId feature, Task 10).
|
||||
/// ParentExecutionId feature).
|
||||
///
|
||||
/// <para>
|
||||
/// <b>Flat list → tree.</b> The repository / query service returns the chain as
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components;
|
||||
/// <para>
|
||||
/// CLAUDE.md mandates UTC throughout the system, but a <c>datetime-local</c>
|
||||
/// value carries no offset, so it must be <i>converted</i> to UTC, not relabelled
|
||||
/// as UTC. Relabelling (the CentralUI-008 bug) shifts every query window by the
|
||||
/// as UTC. Relabelling shifts every query window by the
|
||||
/// user's offset for any non-UTC browser.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
private string _failureMessage = "";
|
||||
private List<TreeNode> _rootNodes = new();
|
||||
|
||||
// Search state (T16): _searchActive distinguishes "ran a search that found
|
||||
// Search state: _searchActive distinguishes "ran a search that found
|
||||
// nothing" from "never searched"; a blank query clears the panel entirely.
|
||||
private string _searchQuery = "";
|
||||
private bool _searchActive;
|
||||
@@ -157,7 +157,7 @@
|
||||
public bool HasChildren { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Friendly DataType name for Variable nodes (T15 type column); null when
|
||||
/// Friendly DataType name for Variable nodes; null when
|
||||
/// not a Variable or the type read failed. Rendered as a muted badge.
|
||||
/// </summary>
|
||||
public string? DataType { get; init; }
|
||||
@@ -261,7 +261,7 @@
|
||||
node.Expanded = true;
|
||||
}
|
||||
|
||||
// Load-more / BrowseNext (T15): fetch the next page under a truncated node,
|
||||
// Load-more / BrowseNext: fetch the next page under a truncated node,
|
||||
// APPEND the children (preserve what's already shown + expanded), and refresh
|
||||
// the stored token (null → exhausted → the button disappears).
|
||||
private async Task LoadMoreAsync(TreeNode node)
|
||||
@@ -297,7 +297,7 @@
|
||||
private Task SearchOnEnter(KeyboardEventArgs e) =>
|
||||
e.Key == "Enter" ? SearchAsync() : Task.CompletedTask;
|
||||
|
||||
// Address-space search (T16): a blank query clears the panel; otherwise run
|
||||
// Address-space search: a blank query clears the panel; otherwise run
|
||||
// the bounded recursive search and render matches as a flat selectable list.
|
||||
private async Task SearchAsync()
|
||||
{
|
||||
|
||||
@@ -349,7 +349,7 @@
|
||||
[Parameter] public bool IsLegacy { get; set; }
|
||||
[Parameter] public ValidationResult? Errors { get; set; }
|
||||
|
||||
// Verify-endpoint context (M7 T17): the site + connection identity the verify
|
||||
// Verify-endpoint context (M7): the site + connection identity the verify
|
||||
// probe targets. Supplied by DataConnectionForm (_formSiteId → SiteIdentifier,
|
||||
// _formName, _protocol). When SiteIdentifier is blank the connection has not been
|
||||
// assigned a site yet, so verification is unavailable.
|
||||
@@ -394,7 +394,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// M7 T17: trust the captured untrusted server certificate at every node of the
|
||||
// M7: trust the captured untrusted server certificate at every node of the
|
||||
// owning site, then re-run Verify (which should now succeed and clear the cert
|
||||
// panel). Administrator-gated by the AuthorizeView wrapping the button; the
|
||||
// CertManagementService enforces the same role server-side-of-the-trust-boundary.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@*
|
||||
Audit Log (#23) M7 Bundle E (T13) — three Health-dashboard KPI tiles for the
|
||||
Three Health-dashboard KPI tiles for the
|
||||
Audit channel: Volume / Error rate / Backlog. Renders Bootstrap card tiles in
|
||||
a single row, each acting as a navigation link to a pre-filtered Audit Log
|
||||
view. The component is purely presentational — the parent page owns the
|
||||
|
||||
@@ -4,11 +4,11 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Health;
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log (#23) M7 Bundle E (T13) code-behind for <see cref="AuditKpiTiles"/>.
|
||||
/// Code-behind for <see cref="AuditKpiTiles"/>.
|
||||
/// Renders three KPI tiles — volume, error rate, backlog — from a
|
||||
/// <see cref="AuditLogKpiSnapshot"/> the parent page supplies. Tiles act as
|
||||
/// drill-in links: clicking navigates to <c>/audit/log</c> with the relevant
|
||||
/// query-string filter pre-applied (Bundle D already parses these params).
|
||||
/// query-string filter pre-applied (the page already parses these params).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
@@ -94,7 +94,7 @@ public partial class AuditKpiTiles
|
||||
{
|
||||
return "—";
|
||||
}
|
||||
// Format to one decimal so a 1-error-in-2000 rate doesn't round to 0%.
|
||||
// Format to one decimal so a rate of 1 error in 2000 doesn't round to 0%.
|
||||
return $"{ErrorRatePercent:0.0}%";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
{
|
||||
<div class="text-muted small mb-3">Site Call KPIs unavailable: @ErrorMessage</div>
|
||||
}
|
||||
@* ── Per-node stuck/parked sub-table (T6: M5.2 per-node stuck-count KPIs) ── *@
|
||||
@* ── Per-node stuck/parked sub-table ── *@
|
||||
@if (HasNodeBreakdown)
|
||||
{
|
||||
<div class="mb-3">
|
||||
|
||||
@@ -5,7 +5,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Health;
|
||||
|
||||
/// <summary>
|
||||
/// Site Call Audit (#22) Task 7 code-behind for <see cref="SiteCallKpiTiles"/>.
|
||||
/// Site Call Audit code-behind for <see cref="SiteCallKpiTiles"/>.
|
||||
/// Renders three KPI tiles — Buffered, Stuck, Parked — from a
|
||||
/// <see cref="SiteCallKpiResponse"/> the parent Health dashboard supplies.
|
||||
/// Tiles act as drill-in links: clicking navigates to <c>/site-calls/report</c>
|
||||
@@ -61,7 +61,7 @@ public partial class SiteCallKpiTiles
|
||||
[Parameter] public string? ErrorMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional per-node KPI breakdown (T6: M5.2 per-node stuck-count KPIs).
|
||||
/// Optional per-node KPI breakdown.
|
||||
/// When non-null and non-empty, a compact node-level stuck/parked sub-table
|
||||
/// is rendered below the main tiles. <c>null</c> means the parent has not
|
||||
/// loaded it yet or has opted out — the sub-table is suppressed entirely.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<NavMenu />
|
||||
</Nav>
|
||||
<RailFooter>
|
||||
@* T34b: dark-mode toggle sits in the rail footer alongside the session
|
||||
@* Dark-mode toggle sits in the rail footer alongside the session
|
||||
block. It is auth-agnostic (pure client-side theme) so it renders even
|
||||
on the login page. *@
|
||||
<DarkModeToggle />
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
|
||||
@* Operations — Secured Writes (M7 T14b). Two-person MxGateway write workflow:
|
||||
@* Operations — Secured Writes. Two-person MxGateway write workflow:
|
||||
Operator submits, a different Verifier approves/rejects. The section must show
|
||||
for Operator OR Verifier. There is no combined policy, so the OR is expressed
|
||||
as: Operator → render; otherwise (NotAuthorized) fall through to a Verifier
|
||||
@@ -124,8 +124,7 @@
|
||||
</AuthorizeView>
|
||||
</NavRailSection>
|
||||
|
||||
@* Audit — gated on the OperationalAudit policy (#23 M7-T15
|
||||
/ Bundle G). Hosts the Audit Log page (#23 M7) and the
|
||||
@* Audit — gated on the OperationalAudit policy. Hosts the Audit Log page (#23 M7) and the
|
||||
Configuration Audit Log (IAuditService config-change
|
||||
viewer). The whole section sits inside the policy block:
|
||||
a non-audit user does not even see the heading.
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
@:Add API Key
|
||||
}
|
||||
</h4>
|
||||
@* Bundle D (#23 M7-T12) drill-in: deep-link into the central Audit Log
|
||||
@* Drill-in: deep-link into the central Audit Log
|
||||
pre-filtered to this API key's inbound calls. Inbound audit rows record
|
||||
the key Name as Actor and live on the ApiInbound channel. *@
|
||||
@if (IsEditMode && !string.IsNullOrWhiteSpace(_formName))
|
||||
@@ -112,7 +112,7 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
// Inbound-API key re-arch (C3): this form drives the IInboundApiKeyAdmin seam.
|
||||
// Inbound-API key re-arch: this form drives the IInboundApiKeyAdmin seam.
|
||||
// Keys are identified by an opaque string KeyId; method access is a set of method
|
||||
// NAMES (scopes) carried on the key, replacing the old ApiMethod.ApprovedApiKeyIds CSV.
|
||||
// The list of all methods still comes from IInboundApiRepository (methods stay in SQL).
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
// Inbound-API key re-arch (C3): this page reads keys from the IInboundApiKeyAdmin seam
|
||||
// Inbound-API key re-arch: this page reads keys from the IInboundApiKeyAdmin seam
|
||||
// (string KeyId, method-scopes) rather than the SQL Server ApiKey entity. The seam has no
|
||||
// retrievable hash, so the old masked Key-Hash column is gone; KeyId identifies each row.
|
||||
private List<InboundApiKeyInfo> _keys = new();
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<h6 class="card-title">@(IsEditMode ? "Edit Site" : "Add Site")</h6>
|
||||
@* Bundle D (#23 M7-T12) drill-in: deep-link into the central Audit
|
||||
@* Drill-in: deep-link into the central Audit
|
||||
Log pre-filtered to this site's events. AuditEvent.SourceSiteId
|
||||
stores the SiteIdentifier (string), so we pass that through. *@
|
||||
@if (IsEditMode && !string.IsNullOrWhiteSpace(_formIdentifier))
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="container-fluid mt-3">
|
||||
<h1 class="h4 mb-3">Audit Log</h1>
|
||||
|
||||
@* Trends panel (M6 / K15). A best-effort collapsible Bootstrap card sitting
|
||||
@* Trends panel. A best-effort collapsible Bootstrap card sitting
|
||||
above the audit query UI: one KpiTrendChart per AuditLog global metric over
|
||||
a 24h (default) / 7d window. Series are fetched independently in the
|
||||
code-behind — a failed fetch degrades only that chart to the unavailable
|
||||
@@ -62,22 +62,22 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
@* Filter bar (Bundle B / M7-T2). Apply hands the collapsed filter to the grid.
|
||||
Bundle D (M7-T10..T12) threads a query-string instance prefill through
|
||||
@* Filter bar. Apply hands the collapsed filter to the grid.
|
||||
Threads a query-string instance prefill through
|
||||
InitialInstanceSearch — UI-only because the filter contract has no instance column. *@
|
||||
<div class="mb-3">
|
||||
<AuditFilterBar OnFilterChanged="HandleFilterChanged"
|
||||
InitialInstanceSearch="@_initialInstanceSearch" />
|
||||
</div>
|
||||
|
||||
@* Export button (Bundle F / M7-T14). A plain <a download> link triggers the
|
||||
@* Export button. A plain <a download> link triggers the
|
||||
streaming CSV endpoint at /api/centralui/audit/export — chosen over a
|
||||
SignalR-driven download because the request can stream 100k rows directly
|
||||
to the response body without buffering through the Blazor circuit. The
|
||||
href reflects the most recently applied filter; before Apply is clicked,
|
||||
an unconstrained export is exposed.
|
||||
|
||||
Bundle G (#23 M7-T15) gates the button on the AuditExport policy so an
|
||||
Gates the button on the AuditExport policy so an
|
||||
OperationalAudit-only operator (read access without bulk export) sees the
|
||||
page + filters but cannot trigger the CSV pull. The endpoint itself is
|
||||
gated separately, so a hand-crafted URL still 403s — the AuthorizeView
|
||||
@@ -96,7 +96,7 @@
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
|
||||
@* Results grid (Bundle B / M7-T3). Row clicks emit OnRowSelected for Bundle C's
|
||||
@* Results grid. Row clicks emit OnRowSelected for the
|
||||
drilldown drawer; the grid stays in "no events" mode until the user applies a
|
||||
filter so the page does not auto-load the full audit table on first render. *@
|
||||
<div>
|
||||
@@ -104,7 +104,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Drilldown drawer (Bundle C / M7-T4..T8). Hosted at the page level so the
|
||||
@* Drilldown drawer. Hosted at the page level so the
|
||||
off-canvas overlay sits above the grid / filter bar irrespective of scroll. *@
|
||||
<AuditDrilldownDrawer Event="@_selectedEvent"
|
||||
IsOpen="@_drawerOpen"
|
||||
|
||||
@@ -11,19 +11,19 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Kpi;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Code-behind for the central Audit Log page (#23 M7). Bundle B (M7-T2 + M7-T3)
|
||||
/// wires up <c>AuditFilterBar</c> and <c>AuditResultsGrid</c>: the page owns the
|
||||
/// Code-behind for the central Audit Log page. Wires up <c>AuditFilterBar</c>
|
||||
/// and <c>AuditResultsGrid</c>: the page owns the
|
||||
/// active <see cref="AuditLogQueryFilter"/> and re-pushes a fresh instance to the
|
||||
/// grid on every Apply (the grid uses reference identity as its "reload"
|
||||
/// trigger). Row clicks land in <see cref="HandleRowSelected"/>, which pins
|
||||
/// the selected row and opens the drilldown drawer.
|
||||
///
|
||||
/// <para>
|
||||
/// Bundle D (M7-T10..T12) adds query-string drill-in parsing so other pages can
|
||||
/// Adds query-string drill-in parsing so other pages can
|
||||
/// deep-link to a pre-filtered Audit Log: <c>?correlationId=</c>, <c>?target=</c>,
|
||||
/// <c>?actor=</c>, <c>?site=</c>, <c>?channel=</c>, <c>?kind=</c>, and the UI-only
|
||||
/// <c>?instance=</c> are read on initialization. Bundle E (M7-T13) extends
|
||||
/// this with <c>?status=</c> so the Health-dashboard Audit error-rate tile can
|
||||
/// <c>?instance=</c> are read on initialization. This extends
|
||||
/// with <c>?status=</c> so the Health-dashboard Audit error-rate tile can
|
||||
/// drill in to <c>?status=Failed</c>. The ExecutionId follow-up adds
|
||||
/// <c>?executionId=</c> for the "View this execution" drill-in, and the
|
||||
/// ParentExecutionId follow-up adds <c>?parentExecutionId=</c> for the
|
||||
@@ -51,7 +51,7 @@ public partial class AuditLogPage : IDisposable
|
||||
[Inject] private NavigationManager Navigation { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// KPI-history facade for the M6 (K15) Trends panel — fetched per metric,
|
||||
/// KPI-history facade for the Trends panel — fetched per metric,
|
||||
/// per window. Best-effort: a failed series fetch degrades that one chart to
|
||||
/// the unavailable placeholder and never breaks the audit query UI.
|
||||
/// </summary>
|
||||
@@ -187,7 +187,7 @@ public partial class AuditLogPage : IDisposable
|
||||
IReadOnlyList<AuditKind>? kinds =
|
||||
AuditQueryParamParsers.ParseEnumList<AuditKind>(Raw(query, "kind"));
|
||||
|
||||
// Bundle E (M7-T13): the Health-dashboard Audit error-rate tile drills in
|
||||
// The Health-dashboard Audit error-rate tile drills in
|
||||
// with ?status=Failed (and operators may craft URLs with Parked/Discarded).
|
||||
// Unknown values are silently dropped — the page still renders without
|
||||
// the constraint.
|
||||
@@ -248,7 +248,7 @@ public partial class AuditLogPage : IDisposable
|
||||
|
||||
private void HandleRowSelected(AuditEventView row)
|
||||
{
|
||||
// Bundle C: a grid row click hands us the full AuditEvent. We pin it as
|
||||
// A grid row click hands us the full AuditEvent. We pin it as
|
||||
// the selected row and open the drilldown drawer — the drawer is fully
|
||||
// presentational so we do not need to refetch the row.
|
||||
_selectedEvent = row;
|
||||
@@ -263,7 +263,7 @@ public partial class AuditLogPage : IDisposable
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// M6 (K15) — Audit Log trend charts.
|
||||
// Audit Log trend charts.
|
||||
//
|
||||
// A best-effort Trends panel that sits above the audit query UI: one
|
||||
// KpiTrendChart per AuditLog global metric, over a 24h (default) or 7d
|
||||
@@ -278,8 +278,7 @@ public partial class AuditLogPage : IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// True while a window's series are being (re)fetched — disables the 24h/7d
|
||||
/// window toggle buttons so a mid-flight click cannot stack overlapping loads
|
||||
/// (mirrors the K13/K14 trend pages).
|
||||
/// window toggle buttons so a mid-flight click cannot stack overlapping loads.
|
||||
/// </summary>
|
||||
private bool _trendsLoading;
|
||||
|
||||
@@ -367,7 +366,7 @@ public partial class AuditLogPage : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bundle F (M7-T14): URL the Export-CSV link points at. Renders the most
|
||||
/// URL the Export-CSV link points at. Renders the most
|
||||
/// recently applied filter as query-string params so the server-side
|
||||
/// streaming endpoint reproduces the user's current view. With no filter
|
||||
/// applied yet, returns the bare endpoint — i.e. an unconstrained export.
|
||||
@@ -394,7 +393,7 @@ public partial class AuditLogPage : IDisposable
|
||||
// No capacity hint: the dimensions are multi-value, so the part count is
|
||||
// unbounded by the number of filter fields.
|
||||
var parts = new List<KeyValuePair<string, string?>>();
|
||||
// Task 9: the filter dimensions are multi-value end-to-end. Emit ONE
|
||||
// The filter dimensions are multi-value end-to-end. Emit ONE
|
||||
// repeated query-string key per selected value (channel=A&channel=B); the
|
||||
// export endpoint's ParseFilter reads the full repeated set.
|
||||
if (filter.Channels is { Count: > 0 } channels)
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@
|
||||
|
||||
<ToastNotification @ref="_toast" />
|
||||
|
||||
@* Bundle Import filter chip (T24). Set via ?bundleImportId={guid} query
|
||||
@* Bundle Import filter chip. Set via ?bundleImportId={guid} query
|
||||
string so drill-ins from the Import wizard / other pages can scope this
|
||||
page to a single import run. Cleared via the × button, which navigates
|
||||
back to the page without the query param so the user sees all rows. *@
|
||||
@@ -192,7 +192,7 @@
|
||||
|
||||
@code {
|
||||
/// <summary>
|
||||
/// T24 (Transport). When non-null, scopes the page to a single bundle
|
||||
/// When non-null, scopes the page to a single bundle
|
||||
/// import run. Set via the <c>?bundleImportId=</c> query string from
|
||||
/// drill-ins (Import wizard summary, future BundleImported row links).
|
||||
/// </summary>
|
||||
@@ -256,7 +256,7 @@
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// T24: when the BundleImportId query param is set (or cleared), refetch
|
||||
// When the BundleImportId query param is set (or cleared), refetch
|
||||
// automatically so the user lands on a pre-filtered page from a drill-in
|
||||
// link without having to click Search.
|
||||
if (BundleImportId != _lastFetchedBundleImportId)
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Code-behind for the execution-chain tree page (Audit Log ParentExecutionId
|
||||
/// feature, Task 10). Route <c>/audit/execution-tree</c>, reached via the Audit
|
||||
/// feature). Route <c>/audit/execution-tree</c>, reached via the Audit
|
||||
/// Log drilldown drawer's "View execution chain" action with
|
||||
/// <c>?executionId={guid}</c>.
|
||||
///
|
||||
@@ -40,7 +40,7 @@ public partial class ExecutionTreePage
|
||||
private bool _loading;
|
||||
private string? _error;
|
||||
|
||||
// Execution-Tree Node Detail Modal feature (Task 4) — state backing the
|
||||
// Execution-Tree Node Detail Modal feature — state backing the
|
||||
// <ExecutionDetailModal>. A double-click on a tree node sets
|
||||
// _modalExecutionId + flips _modalOpen true; the modal loads that
|
||||
// execution's audit rows on the closed → open transition. _modalOpen is the
|
||||
|
||||
@@ -4,8 +4,8 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Deployment;
|
||||
|
||||
/// <summary>
|
||||
/// Pure (no Blazor/DI) builder that turns a flat list of streamed attribute (and,
|
||||
/// in DV-4, alarm) events into a collapsible composition forest of
|
||||
/// Pure (no Blazor/DI) builder that turns a flat list of streamed attribute (and
|
||||
/// alarm) events into a collapsible composition forest of
|
||||
/// <see cref="DebugTreeNode"/>. Path-qualified canonical names are split on
|
||||
/// <c>'.'</c> to derive branch nodes; the terminal segment carries the payload.
|
||||
/// </summary>
|
||||
|
||||
@@ -4,7 +4,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums; // AlarmState
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Deployment;
|
||||
|
||||
/// <summary>
|
||||
/// A node in a Debug View composition tree. Attribute (and, in DV-4, alarm)
|
||||
/// A node in a Debug View composition tree. Attribute (and alarm)
|
||||
/// names are path-qualified canonical names (e.g. <c>Motor1.Compressor.Pump</c>);
|
||||
/// the tree is derived by splitting those names on <c>'.'</c>. A node is either a
|
||||
/// branch (composition member, no payload, has children) or a leaf (one attribute
|
||||
@@ -22,9 +22,9 @@ public sealed class DebugTreeNode
|
||||
|
||||
// Leaf payloads — exactly one is set on a leaf; both null on a pure branch.
|
||||
public AttributeValueChanged? Attribute { get; init; }
|
||||
public AlarmStateChanged? Alarm { get; init; } // computed leaf, native condition, or placeholder (DV-4)
|
||||
public AlarmStateChanged? Alarm { get; init; } // computed leaf, native condition, or placeholder
|
||||
|
||||
public bool IsNativeBinding { get; init; } // branch grouping native conditions (DV-4)
|
||||
public bool IsNativeBinding { get; init; } // branch grouping native conditions
|
||||
|
||||
// Roll-up (set by the builder for branch nodes).
|
||||
public AlarmState WorstState { get; set; } = AlarmState.Normal;
|
||||
|
||||
+4
-4
@@ -25,7 +25,7 @@
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<button class="btn btn-outline-secondary btn-sm me-3" @onclick="GoBack">← Back to Topology</button>
|
||||
<h4 class="mb-0">Configure Instance</h4>
|
||||
@* Bundle D (#23 M7-T12) drill-in: deep-link into the central Audit Log
|
||||
@* Drill-in: deep-link into the central Audit Log
|
||||
pre-filtered to this instance. Instance is UI-only on the filter bar
|
||||
(AuditEvent has no Instance column), so we use the ?instance= UI-text
|
||||
seam — the filter bar's Instance free-text input is pre-populated. *@
|
||||
@@ -178,7 +178,7 @@
|
||||
<div class="card mb-3">
|
||||
<div class="card-header py-2 d-flex justify-content-between align-items-center">
|
||||
<strong>Attribute Overrides</strong>
|
||||
@* M7-T16: bulk import of attribute overrides from a CSV
|
||||
@* Bulk import of attribute overrides from a CSV
|
||||
(AttributeName,Value[,ElementType]). Selecting a file parses +
|
||||
validates it against this instance's overridable attributes and —
|
||||
all-or-nothing — applies every row through the SAME
|
||||
@@ -598,7 +598,7 @@
|
||||
// un-parseable List element caught on the pre-submit round-trip).
|
||||
private Dictionary<string, string> _overrideErrors = new();
|
||||
|
||||
// M7-T16: CSV bulk-import result summary. _csvImportResult is the headline
|
||||
// CSV bulk-import result summary. _csvImportResult is the headline
|
||||
// ("Imported N overrides." or "Import rejected — N error(s)."); on failure the
|
||||
// per-line messages are listed from _csvImportErrors. Null until an import runs.
|
||||
private string? _csvImportResult;
|
||||
@@ -1047,7 +1047,7 @@
|
||||
_saving = false;
|
||||
}
|
||||
|
||||
// ── M7-T16: CSV bulk override import ────────────────────
|
||||
// ── CSV bulk override import ────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Handles a selected override CSV. Reads the file text (size-capped), parses it
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Deployment;
|
||||
|
||||
/// <summary>
|
||||
/// Code-behind for <c>InstanceConfigure.razor</c> — hosts the pure, side-effect-free
|
||||
/// core of the CSV bulk-override import (M7-T16) so it can be unit-pinned without
|
||||
/// core of the CSV bulk-override import so it can be unit-pinned without
|
||||
/// standing up the page's ≈7 injected services. The Razor side reads the uploaded
|
||||
/// file's text, calls <see cref="OverrideCsvParser.Parse"/>, then feeds the result
|
||||
/// here; on success it applies the returned dict through the SAME
|
||||
|
||||
@@ -198,7 +198,7 @@
|
||||
private IReadOnlyList<ZB.MOM.WW.ScadaBridge.CentralUI.ScriptAnalysis.DiagnosticMarker> _markers
|
||||
= Array.Empty<ZB.MOM.WW.ScadaBridge.CentralUI.ScriptAnalysis.DiagnosticMarker>();
|
||||
|
||||
// Inbound-API key re-arch (C3): the approved-keys list is driven by the IInboundApiKeyAdmin
|
||||
// Inbound-API key re-arch: the approved-keys list is driven by the IInboundApiKeyAdmin
|
||||
// seam, not ApiMethod.ApprovedApiKeyIds. The ApiMethod entity itself (name/script/params/etc.)
|
||||
// still lives on IInboundApiRepository — only the key↔method approval relationship moved to
|
||||
// per-key method-scopes. Keys are identified by an opaque string KeyId.
|
||||
|
||||
@@ -213,7 +213,7 @@
|
||||
|
||||
private ToastNotification _toast = default!;
|
||||
|
||||
// M9-T25: live per-connection health, keyed by DataConnection.Id. Sourced from
|
||||
// Live per-connection health, keyed by DataConnection.Id. Sourced from
|
||||
// the existing site→central health transport via IConnectionHealthQueryService
|
||||
// and refreshed on a ~10s poll, mirroring the Health dashboard's timer pattern.
|
||||
// A connection absent from the map renders an "Unknown" badge (tolerates a site
|
||||
@@ -419,7 +419,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ── M9-T24b / T33b: Move connection to another site ──
|
||||
// ── Move connection to another site ──
|
||||
// Opened via IDialogService.ShowAsync. The body dispatches MoveDataConnectionCommand
|
||||
// through the guard-running ManagementActor path (IDataConnectionMoveService) — NOT
|
||||
// a direct repository write — so the server enforces the Designer gate and every
|
||||
@@ -451,7 +451,7 @@
|
||||
.Select(r => (r.SiteId!.Value, r.Label))
|
||||
.ToList();
|
||||
|
||||
// M9-T25: enum → Bootstrap badge class. Mirrors the Health dashboard's
|
||||
// Enum → Bootstrap badge class. Mirrors the Health dashboard's
|
||||
// GetConnectionHealthBadge (Components/Pages/Monitoring/Health.razor) so the
|
||||
// design page surfaces the same colour coding for the same status. Kept as a
|
||||
// small local mirror — the Health helper is a private page-local method, not a
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4 class="mb-0">@(Id.HasValue ? "Edit External System" : "Add External System")</h4>
|
||||
@* Bundle D (#23 M7-T12) drill-in: deep-link into the central Audit Log
|
||||
@* Drill-in: deep-link into the central Audit Log
|
||||
pre-filtered to this external system's outbound API events. Audit rows
|
||||
record the target by external-system name, so we filter on Target. *@
|
||||
@if (Id.HasValue && !string.IsNullOrWhiteSpace(_name))
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
@inject ISchemaLibraryService SchemaLibraryService
|
||||
@inject IDialogService Dialog
|
||||
|
||||
@* Schema Library (M9-T32c): list + create/edit (via SchemaBuilder) + delete the
|
||||
@* Schema Library: list + create/edit (via SchemaBuilder) + delete the
|
||||
reusable named JSON-Schema library entries that the {"$ref":"lib:Name"} resolver
|
||||
(T32b) resolves against. Every mutation is dispatched through ISchemaLibraryService
|
||||
resolves against. Every mutation is dispatched through ISchemaLibraryService
|
||||
— the guard-running ManagementActor path — never a direct repo write. *@
|
||||
|
||||
<div class="container-fluid mt-3">
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
private Template? _ownerTemplate;
|
||||
private TemplateComposition? _ownerComposition;
|
||||
|
||||
// M9-T26b: the FULL transitively-resolved inherited member set (multi-level
|
||||
// M9: the FULL transitively-resolved inherited member set (multi-level
|
||||
// chain + post-creation base additions) + staleness, fetched read-only via
|
||||
// GetResolvedTemplateMembersCommand. Populated only for derived templates;
|
||||
// null when the read failed (editor falls back to the stored-row view).
|
||||
@@ -228,7 +228,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// M9-T26b: resolve the FULL inherited member set (whole chain +
|
||||
// M9: resolve the FULL inherited member set (whole chain +
|
||||
// post-creation base additions) read-only. The stored-row tables
|
||||
// above only carry the IMMEDIATE base; this surfaces the rest with
|
||||
// origin annotation + a base-changed staleness banner. Read-only:
|
||||
@@ -314,7 +314,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@* M9-T26b: the FULL transitively-resolved inherited member set — the whole
|
||||
@* M9: the FULL transitively-resolved inherited member set — the whole
|
||||
chain (grandparent + further ancestors) plus base members added after
|
||||
this template was created, which the immediate-base tables below cannot
|
||||
show. Read-only preview; each row carries its origin template + lock
|
||||
@@ -473,7 +473,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
// ---- M9-T26b: full resolved inherited set (read-only preview) ----
|
||||
// ---- M9: full resolved inherited set (read-only preview) ----
|
||||
|
||||
/// <summary>
|
||||
/// Renders the FULL transitively-resolved effective member set (attributes,
|
||||
|
||||
@@ -425,7 +425,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Sibling reorder (T23b) ----
|
||||
// ---- Sibling reorder ----
|
||||
// Dispatches the reorder the same way Move/Rename do — directly through
|
||||
// TemplateFolderService (the backend swaps SortOrder with the adjacent
|
||||
// same-parent sibling, no-op at the ends) — then reloads so the new order
|
||||
@@ -471,7 +471,7 @@
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// ---- Root-level context menu (T23b) ----
|
||||
// ---- Root-level context menu ----
|
||||
// Right-click on the tree zone (empty space or below the tree) offers
|
||||
// New Folder / New Template at root. Node right-clicks are handled by the
|
||||
// TreeView's own context menu and never reach here.
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@attribute [Authorize(Policy = AuthorizationPolicies.RequireDesign)]
|
||||
|
||||
@*
|
||||
TransportExport wizard (Component #24, Task T21).
|
||||
TransportExport wizard (Component #24).
|
||||
|
||||
A 4-step linear wizard:
|
||||
Step 1 — Select : templates (tree, checkbox-mode) + flat artifact lists.
|
||||
@@ -138,7 +138,7 @@
|
||||
@RenderCheckboxList(_smtpConfigs, s => s.Id, s => s.Host, _selectedSmtpConfigs)
|
||||
</fieldset>
|
||||
|
||||
@* S10c: SMS provider configs, mirroring the SMTP section above. Labelled by
|
||||
@* SMS provider configs, mirroring the SMTP section above. Labelled by
|
||||
AccountSid (the bundle key); the secret AuthToken is never rendered. *@
|
||||
<fieldset class="mb-4" data-testid="group-sms-configs">
|
||||
<legend class="h6">SMS Configurations</legend>
|
||||
@@ -361,12 +361,12 @@
|
||||
{
|
||||
<li>SmtpConfig: @s.Host</li>
|
||||
}
|
||||
@* S10c: SMS configs in the closure, mirroring SmtpConfig above; AccountSid only, never AuthToken. *@
|
||||
@* SMS configs in the closure, mirroring SmtpConfig above; AccountSid only, never AuthToken. *@
|
||||
@foreach (var s in _resolved.SmsConfigs.OrderBy(s => s.AccountSid, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
<li>SmsConfig: @s.AccountSid</li>
|
||||
}
|
||||
@* Inbound API keys are not transported (re-arch C4) — methods only. *@
|
||||
@* Inbound API keys are not transported — methods only. *@
|
||||
@foreach (var m in _resolved.ApiMethods.OrderBy(m => m.Name, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
<li>ApiMethod: @m.Name</li>
|
||||
|
||||
+15
-15
@@ -20,7 +20,7 @@ using ZB.MOM.WW.ScadaBridge.Transport.Export;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Design;
|
||||
|
||||
/// <summary>
|
||||
/// Code-behind for the TransportExport wizard (Transport feature, Task T21).
|
||||
/// Code-behind for the TransportExport wizard (Transport feature).
|
||||
///
|
||||
/// Four-step state machine:
|
||||
/// <list type="number">
|
||||
@@ -72,12 +72,12 @@ public partial class TransportExport : ComponentBase
|
||||
private List<DatabaseConnectionDefinition> _dbConnections = new();
|
||||
private List<NotificationList> _notificationLists = new();
|
||||
private List<SmtpConfiguration> _smtpConfigs = new();
|
||||
// S10c: SMS provider configs, mirroring _smtpConfigs. Keyed by AccountSid in the
|
||||
// SMS provider configs, mirroring _smtpConfigs. Keyed by AccountSid in the
|
||||
// bundle; the wizard selects by entity id (same as SMTP) and never surfaces AuthToken.
|
||||
private List<SmsConfiguration> _smsConfigs = new();
|
||||
// Inbound API keys are not transported between environments (re-arch C4); only methods.
|
||||
// Inbound API keys are not transported between environments; only methods.
|
||||
private List<ApiMethod> _apiMethods = new();
|
||||
// M8 (E1): site/instance-scoped export. Sites are listed flat; each site's
|
||||
// Site/instance-scoped export. Sites are listed flat; each site's
|
||||
// instances hang off it (loaded eagerly via ISiteRepository.GetInstancesBySiteIdAsync)
|
||||
// so the operator can pick a whole site or drill into individual instances.
|
||||
private List<Site> _sites = new();
|
||||
@@ -93,11 +93,11 @@ public partial class TransportExport : ComponentBase
|
||||
private readonly HashSet<int> _selectedDbConnections = new();
|
||||
private readonly HashSet<int> _selectedNotificationLists = new();
|
||||
private readonly HashSet<int> _selectedSmtpConfigs = new();
|
||||
// S10c: SMS provider config selection, mirroring _selectedSmtpConfigs.
|
||||
// SMS provider config selection, mirroring _selectedSmtpConfigs.
|
||||
private readonly HashSet<int> _selectedSmsConfigs = new();
|
||||
// No _selectedApiKeys: inbound API keys are not transported (re-arch C4).
|
||||
// No _selectedApiKeys: inbound API keys are not transported.
|
||||
private readonly HashSet<int> _selectedApiMethods = new();
|
||||
// M8 (E1): site/instance selection backed by entity primary keys, matching the
|
||||
// Site/instance selection backed by entity primary keys, matching the
|
||||
// DependencyResolver's SiteIds/InstanceIds (NOT names — the resolver fetches by id).
|
||||
private readonly HashSet<int> _selectedSites = new();
|
||||
private readonly HashSet<int> _selectedInstances = new();
|
||||
@@ -143,12 +143,12 @@ public partial class TransportExport : ComponentBase
|
||||
_dbConnections = (await ExternalRepo.GetAllDatabaseConnectionsAsync()).ToList();
|
||||
_notificationLists = (await NotificationRepo.GetAllNotificationListsAsync()).ToList();
|
||||
_smtpConfigs = (await NotificationRepo.GetAllSmtpConfigurationsAsync()).ToList();
|
||||
// S10c: SMS provider configs, mirroring the SMTP load path above.
|
||||
// SMS provider configs, mirroring the SMTP load path above.
|
||||
_smsConfigs = (await NotificationRepo.GetAllSmsConfigurationsAsync()).ToList();
|
||||
// Inbound API keys are not transported (re-arch C4) — only methods are loaded.
|
||||
// Inbound API keys are not transported — only methods are loaded.
|
||||
_apiMethods = (await InboundApiRepo.GetAllApiMethodsAsync()).ToList();
|
||||
|
||||
// M8 (E1): sites + their instances for site/instance-scoped export. Each
|
||||
// Sites + their instances for site/instance-scoped export. Each
|
||||
// site's instances are loaded eagerly so the expandable picker has them
|
||||
// without a per-click round-trip; sites are ordered by identifier to match
|
||||
// the bundle's deterministic site ordering.
|
||||
@@ -245,17 +245,17 @@ public partial class TransportExport : ComponentBase
|
||||
DatabaseConnectionIds: _selectedDbConnections.ToList(),
|
||||
NotificationListIds: _selectedNotificationLists.ToList(),
|
||||
SmtpConfigurationIds: _selectedSmtpConfigs.ToList(),
|
||||
// Inbound API keys are not transported (re-arch C4) — methods only.
|
||||
// Inbound API keys are not transported — methods only.
|
||||
ApiMethodIds: _selectedApiMethods.ToList(),
|
||||
IncludeDependencies: _includeDependencies)
|
||||
{
|
||||
// M8 (E1): site/instance ids feed the resolver's SiteIds/InstanceIds.
|
||||
// Site/instance ids feed the resolver's SiteIds/InstanceIds.
|
||||
// Set via init-only properties so the positional ctor stays the documented
|
||||
// additive shape; the resolver dedups a selected instance against its
|
||||
// already-selected owning site.
|
||||
SiteIds = _selectedSites.ToList(),
|
||||
InstanceIds = _selectedInstances.ToList(),
|
||||
// S10c: SMS provider config ids feed the resolver's SmsConfigurationIds,
|
||||
// SMS provider config ids feed the resolver's SmsConfigurationIds,
|
||||
// exactly as SmtpConfigurationIds above (init-only, trailing additive shape).
|
||||
SmsConfigurationIds = _selectedSmsConfigs.ToList(),
|
||||
};
|
||||
@@ -325,7 +325,7 @@ public partial class TransportExport : ComponentBase
|
||||
{
|
||||
if (!string.IsNullOrEmpty(smtp.Credentials)) count++;
|
||||
}
|
||||
// S10c: SMS provider AuthToken is a secret, mirroring SMTP Credentials above.
|
||||
// SMS provider AuthToken is a secret, mirroring SMTP Credentials above.
|
||||
foreach (var sms in resolved.SmsConfigs)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sms.AuthToken)) count++;
|
||||
@@ -485,7 +485,7 @@ public partial class TransportExport : ComponentBase
|
||||
else set.Remove(id);
|
||||
}
|
||||
|
||||
// ---- Step 1 site/instance helpers (M8 E1) ----
|
||||
// ---- Step 1 site/instance helpers ----
|
||||
|
||||
/// <summary>Instances loaded for a site, or an empty list when the site has none.</summary>
|
||||
private IReadOnlyList<Instance> InstancesFor(int siteId) =>
|
||||
|
||||
+14
-14
@@ -17,7 +17,7 @@ using ZB.MOM.WW.ScadaBridge.Transport.Import;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Design;
|
||||
|
||||
/// <summary>
|
||||
/// Code-behind for the TransportImport wizard (Transport feature, Task T22).
|
||||
/// Code-behind for the TransportImport wizard (Transport feature).
|
||||
///
|
||||
/// Five-step state machine:
|
||||
/// <list type="number">
|
||||
@@ -42,7 +42,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Design;
|
||||
///
|
||||
/// Cached bundle bytes: because <see cref="IBundleImporter.LoadAsync"/> currently
|
||||
/// peeks the manifest by attempting decryption, encrypted bundles require two
|
||||
/// LoadAsync invocations. CentralUI-031: we previously cached the raw bytes in a
|
||||
/// LoadAsync invocations. We previously cached the raw bytes in a
|
||||
/// <c>byte[] _bundleBytes</c> field, which buffered the full upload (default cap
|
||||
/// 100 MB) in the component's per-circuit state — multiplied across concurrent
|
||||
/// operator sessions, that produced real central-node memory pressure. The
|
||||
@@ -71,7 +71,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
[Inject] private IAuditService AuditService { get; set; } = default!;
|
||||
[Inject] private ScadaBridgeDbContext DbContext { get; set; } = default!;
|
||||
|
||||
// M8 E2: the Map step needs the destination environment's sites + each
|
||||
// The Map step needs the destination environment's sites + each
|
||||
// site's connections to populate the "map to existing target" dropdowns.
|
||||
[Inject] private ISiteRepository SiteRepo { get; set; } = default!;
|
||||
|
||||
@@ -80,7 +80,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
private string? _errorMessage;
|
||||
|
||||
// ---- Session + cached bundle path ----
|
||||
// CentralUI-031: the upload is streamed to a per-session temp file and only
|
||||
// The upload is streamed to a per-session temp file and only
|
||||
// the path is retained on the component, so we don't hold an entire bundle
|
||||
// (up to MaxBundleSizeMb, default 100 MB) in per-circuit memory across the
|
||||
// wizard's lifetime. The file is deleted on every wizard reset path and on
|
||||
@@ -105,7 +105,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
// Keyed by (EntityType, Name) — matches BundleImporter.ApplyAsync's lookup.
|
||||
private Dictionary<(string EntityType, string Name), ImportResolution>? _resolutions;
|
||||
|
||||
// ---- Step 3 (Map sub-section, M8 E2): name mapping ----
|
||||
// ---- Step 3 (Map sub-section): name mapping ----
|
||||
// The sentinel dropdown value for "Create new" — empty string can't collide
|
||||
// with a real SiteIdentifier / connection Name (both are non-empty).
|
||||
private const string CreateNewValue = "";
|
||||
@@ -160,7 +160,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
// CentralUI-031: stream the upload directly to a per-session temp
|
||||
// Stream the upload directly to a per-session temp
|
||||
// file so the central node's working set is bounded by the
|
||||
// FileStream buffer (~80 KB) rather than the full bundle bytes.
|
||||
// OpenReadStream's MaxAllowedSize defaults to 500_000 bytes — bump
|
||||
@@ -209,7 +209,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
}
|
||||
try
|
||||
{
|
||||
// CentralUI-031: read the staged bundle straight off disk; the
|
||||
// Read the staged bundle straight off disk; the
|
||||
// importer's LoadAsync only walks the stream forward, so a plain
|
||||
// FileStream is sufficient (no need to buffer it back into memory).
|
||||
using var stream = new FileStream(
|
||||
@@ -435,7 +435,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// M8 E2: prepares the Map sub-section state for a freshly loaded preview.
|
||||
/// Prepares the Map sub-section state for a freshly loaded preview.
|
||||
/// When the preview carries no required site/connection mappings this is a
|
||||
/// no-op (the Map section is hidden) and we skip the site/connection reads
|
||||
/// entirely — central-config-only bundles never touch the destination's
|
||||
@@ -564,7 +564,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// M8 E2: folds the operator's Map-step choices into a <see cref="BundleNameMap"/>.
|
||||
/// Folds the operator's Map-step choices into a <see cref="BundleNameMap"/>.
|
||||
/// A concrete chosen target → <see cref="MappingAction.MapToExisting"/> with that
|
||||
/// target identifier/name; the "Create new" sentinel → <see cref="MappingAction.CreateNew"/>
|
||||
/// with a null target. Iterates the preview's required mappings so the map's shape
|
||||
@@ -607,7 +607,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// M8 E2: parses a Modified item's <c>FieldDiffJson</c> and returns the
|
||||
/// Parses a Modified item's <c>FieldDiffJson</c> and returns the
|
||||
/// <c>lineDiff</c> object for the first code field that carries one, or null
|
||||
/// when the diff has no line-level payload (ordinary fields render as a coarse
|
||||
/// summary instead). Tolerant of malformed/absent JSON — a parse failure
|
||||
@@ -772,7 +772,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
try
|
||||
{
|
||||
var user = await Auth.GetCurrentUsernameAsync();
|
||||
// M8 E2: fold the operator's Map-step choices into the name map. For
|
||||
// Fold the operator's Map-step choices into the name map. For
|
||||
// central-config-only bundles (no required mappings) this is
|
||||
// BundleNameMap.Empty, which the importer normalises away.
|
||||
var nameMap = BuildNameMap();
|
||||
@@ -814,7 +814,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
_confirmEnvironmentText = string.Empty;
|
||||
_result = null;
|
||||
_validationErrors = null;
|
||||
// M8 E2: clear the Map sub-section state too.
|
||||
// Clear the Map sub-section state too.
|
||||
_targetSites = Array.Empty<Site>();
|
||||
_targetConnections.Clear();
|
||||
_siteChoices.Clear();
|
||||
@@ -822,7 +822,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI-031: deletes the staged bundle temp file if any. Swallows IO
|
||||
/// Deletes the staged bundle temp file if any. Swallows IO
|
||||
/// failures — an undeletable temp file is best-effort cleanup and must not
|
||||
/// block the wizard.
|
||||
/// </summary>
|
||||
@@ -852,7 +852,7 @@ public partial class TransportImport : ComponentBase, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI-031: ensures the staged temp file does not survive circuit
|
||||
/// Ensures the staged temp file does not survive circuit
|
||||
/// teardown. Blazor invokes Dispose when the user navigates away or the
|
||||
/// circuit ends, so an abandoned wizard cleans up automatically.
|
||||
/// </summary>
|
||||
|
||||
@@ -72,13 +72,13 @@
|
||||
PerNodeSnapshots="@_siteCallNodeKpis"
|
||||
PerNodeAvailable="@_siteCallNodeKpiAvailable" />
|
||||
|
||||
@* Audit Log (#23) M7 Bundle E — three KPI tiles for the Audit channel
|
||||
@* Three KPI tiles for the Audit channel
|
||||
(volume / error rate / backlog). Refreshed alongside the site states. *@
|
||||
<AuditKpiTiles Snapshot="@_auditKpi"
|
||||
IsAvailable="@_auditKpiAvailable"
|
||||
ErrorMessage="@_auditKpiError" />
|
||||
|
||||
@* Site Health Trends (M6 K16) — per-site Site Health KPI history. Loads on a
|
||||
@* Site Health Trends (M6) — per-site Site Health KPI history. Loads on a
|
||||
separate path from the 10s tile-refresh timer so a trend-query fault can
|
||||
never disturb the live dashboard or its polling loop. The site selector
|
||||
reuses the site keys already loaded into _siteStates; the window toggle
|
||||
@@ -438,7 +438,7 @@
|
||||
private bool _outboxKpiAvailable;
|
||||
private string? _outboxKpiError;
|
||||
|
||||
// Audit Log (#23) M7 Bundle E — Audit KPI tiles. Volume + error rate come
|
||||
// Audit KPI tiles. Volume + error rate come
|
||||
// from a 1h aggregate over the central AuditLog table; backlog sums the
|
||||
// per-site SiteAuditBacklog.PendingCount via the health aggregator.
|
||||
private AuditLogKpiSnapshot? _auditKpi;
|
||||
@@ -452,13 +452,13 @@
|
||||
private bool _siteCallKpiAvailable;
|
||||
private string? _siteCallKpiError;
|
||||
|
||||
// Per-node Site Call KPI breakdown (T6: M5.2 per-node stuck-count KPIs).
|
||||
// Per-node Site Call KPI breakdown (M5.2 per-node stuck-count KPIs).
|
||||
// Passed to SiteCallKpiTiles as an optional sub-table.
|
||||
private IReadOnlyList<SiteCallNodeKpiSnapshot> _siteCallNodeKpis =
|
||||
Array.Empty<SiteCallNodeKpiSnapshot>();
|
||||
private bool _siteCallNodeKpiAvailable;
|
||||
|
||||
// ── Site Health Trends (M6 K16) ───────────────────────────────────────────
|
||||
// ── Site Health Trends (M6) ───────────────────────────────────────────────
|
||||
// Per-site Site Health KPI history, loaded on a path entirely separate from
|
||||
// the 10s tile-refresh timer (LoadSiteHealthTrendsAsync, never called from
|
||||
// the timer tick). The site keys are a snapshot of the dashboard's site set,
|
||||
@@ -512,7 +512,7 @@
|
||||
|
||||
await RefreshNow();
|
||||
|
||||
// Site Health Trends (M6 K16) load on their own path — never from the
|
||||
// Site Health Trends (M6) load on their own path — never from the
|
||||
// timer tick below — so a trend-query fault can't disturb the live tile
|
||||
// refresh. Seed the selector from the sites just loaded into _siteStates
|
||||
// and query the default site.
|
||||
@@ -694,7 +694,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Per-node site-call KPI loader (T6: M5.2). Best-effort; a fault silently
|
||||
// Per-node site-call KPI loader (M5.2). Best-effort; a fault silently
|
||||
// suppresses the per-node sub-table rather than degrading the dashboard.
|
||||
private async Task LoadSiteCallNodeKpis()
|
||||
{
|
||||
|
||||
+5
-5
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@* ── Per-node breakdown (T6: additive) ── *@
|
||||
@* ── Per-node breakdown (additive) ── *@
|
||||
<h5 class="mb-2">Per-node breakdown</h5>
|
||||
@if (_perNodeError != null)
|
||||
{
|
||||
@@ -162,7 +162,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@* ── Trends (T11: first KPI-history consumer) ── *@
|
||||
@* ── Trends (first KPI-history consumer) ── *@
|
||||
<div class="d-flex justify-content-between align-items-center mt-4 mb-2" data-test="notification-trends">
|
||||
<h5 class="mb-0">Trends</h5>
|
||||
<div class="btn-group btn-group-sm" role="group" aria-label="Trend window">
|
||||
@@ -205,15 +205,15 @@
|
||||
private IReadOnlyList<SiteNotificationKpiSnapshot> _perSite = Array.Empty<SiteNotificationKpiSnapshot>();
|
||||
private string? _perSiteError;
|
||||
|
||||
// ── Per-node (T6: M5.2 per-node stuck-count KPIs) ──
|
||||
// ── Per-node (M5.2 per-node stuck-count KPIs) ──
|
||||
private IReadOnlyList<NodeNotificationKpiSnapshot> _perNode = Array.Empty<NodeNotificationKpiSnapshot>();
|
||||
private string? _perNodeError;
|
||||
|
||||
private bool _loading;
|
||||
|
||||
// ── Trends (T11: first KPI-history consumer) ──
|
||||
// ── Trends (first KPI-history consumer) ──
|
||||
// Window in hours: 24h (default) or 168h (7d). Toggling re-queries.
|
||||
// Per-metric isolation (mirrors the K14 SiteCallsReport pattern): each metric
|
||||
// Per-metric isolation (mirrors the SiteCallsReport pattern): each metric
|
||||
// carries its own series + availability + error, each loaded via its own
|
||||
// try/catch so one metric's failure only blanks that one chart and the others
|
||||
// still render their already-fetched data.
|
||||
|
||||
+1
-1
@@ -172,7 +172,7 @@
|
||||
<td><TimestampDisplay Value="@n.CreatedAt" Format="yyyy-MM-dd HH:mm" /></td>
|
||||
<td><TimestampDisplay Value="@n.DeliveredAt" Format="yyyy-MM-dd HH:mm" NullText="—" /></td>
|
||||
<td class="text-end" @ondblclick:stopPropagation="true">
|
||||
@* Bundle D (#23 M7-T10) drill-in: NotificationId is the audit
|
||||
@* NotificationId is the audit
|
||||
CorrelationId, so the link deep-links into the central Audit
|
||||
Log pre-filtered to this notification's lifecycle events. *@
|
||||
<a class="btn btn-outline-secondary btn-sm me-1"
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
@inject IDialogService Dialog
|
||||
@inject ILogger<SecuredWrites> Logger
|
||||
|
||||
@* Secured Writes (M7 OPC UA / MxGateway UX, Task C5 / T14b). Two-person workflow:
|
||||
@* Secured Writes. Two-person workflow:
|
||||
an Operator submits a write against an MxGateway connection; a DIFFERENT Verifier
|
||||
approves (which relays the write to the device) or rejects. This page only SUBMITS
|
||||
commands — the server (ManagementActor) enforces roles, the no-self-approval guard,
|
||||
|
||||
+1
-1
@@ -224,7 +224,7 @@
|
||||
OnNext="NextPage" />
|
||||
}
|
||||
|
||||
@* ── Trends (K14: collapsible KPI-history charts) ──
|
||||
@* ── Trends (collapsible KPI-history charts) ──
|
||||
A best-effort section — each chart's GetSeriesAsync is wrapped so a KPI
|
||||
backend hiccup renders the chart unavailable rather than breaking the
|
||||
grid above. The series load lazily on first expand (and re-load on the
|
||||
|
||||
+8
-8
@@ -11,7 +11,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Kpi;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.SiteCalls;
|
||||
|
||||
/// <summary>
|
||||
/// Code-behind for the central Site Calls report page (Site Call Audit #22). A
|
||||
/// Code-behind for the central Site Calls report page. A
|
||||
/// near-mirror of <see cref="ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Notifications.NotificationReport"/>:
|
||||
/// it queries the central <c>SiteCalls</c> table via
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Communication.CommunicationService.QuerySiteCallsAsync"/>,
|
||||
@@ -60,7 +60,7 @@ public partial class SiteCallsReport
|
||||
|
||||
private ToastNotification _toast = default!;
|
||||
private List<Site> _sites = new();
|
||||
// CentralUI-028: unfiltered site list so a permitted-site lookup resolves
|
||||
// Unfiltered site list so a permitted-site lookup resolves
|
||||
// correctly for a SourceSite whose Site was filtered out of the dropdown.
|
||||
private List<Site> _allSites = new();
|
||||
private bool _siteScopeSystemWide;
|
||||
@@ -96,7 +96,7 @@ public partial class SiteCallsReport
|
||||
private DateTime? _fromFilter;
|
||||
private DateTime? _toFilter;
|
||||
|
||||
// ── Trends (K14) ──────────────────────────────────────────────────────────
|
||||
// ── Trends ──────────────────────────────────────────────────────────────
|
||||
// Collapsible KPI-history charts for the SiteCallAudit / Global series. The
|
||||
// section is collapsed on init and the series load lazily on first expand
|
||||
// (and on each 24h/7d window toggle), so the page's primary job — the call
|
||||
@@ -127,7 +127,7 @@ public partial class SiteCallsReport
|
||||
try
|
||||
{
|
||||
_allSites = (await SiteRepository.GetAllSitesAsync()).ToList();
|
||||
// CentralUI-028: restrict the source-site dropdown to the user's
|
||||
// Restrict the source-site dropdown to the user's
|
||||
// permitted set. System-wide users see the full list back unchanged.
|
||||
_sites = await SiteScope.FilterSitesAsync(_allSites);
|
||||
_siteScopeSystemWide = await SiteScope.IsSystemWideAsync();
|
||||
@@ -249,7 +249,7 @@ public partial class SiteCallsReport
|
||||
var response = await CommunicationService.QuerySiteCallsAsync(request);
|
||||
if (response.Success)
|
||||
{
|
||||
// CentralUI-028: drop any row whose source site is outside the
|
||||
// Drop any row whose source site is outside the
|
||||
// user's permitted set, as a row-level safety net behind the
|
||||
// dropdown restriction.
|
||||
_siteCalls = await FilterPermittedAsync(response.SiteCalls);
|
||||
@@ -278,7 +278,7 @@ public partial class SiteCallsReport
|
||||
|
||||
private async Task RetrySiteCall(SiteCallSummary c)
|
||||
{
|
||||
// CentralUI-028: server-side re-check before relaying — a Retry relay must
|
||||
// Server-side re-check before relaying — a Retry relay must
|
||||
// not fire for a site outside the caller's permitted set, even if the row
|
||||
// somehow appeared in the grid.
|
||||
if (!await IsRowSiteAllowedAsync(c.SourceSite))
|
||||
@@ -473,7 +473,7 @@ public partial class SiteCallsReport
|
||||
private static string? NullIfEmpty(string s) => string.IsNullOrWhiteSpace(s) ? null : s.Trim();
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI-027: <c><input type="datetime-local"></c> binds with
|
||||
/// <c><input type="datetime-local"></c> binds with
|
||||
/// <see cref="DateTimeKind.Unspecified"/> and the value is the operator's
|
||||
/// browser-local wall-clock. Tag it <see cref="DateTimeKind.Local"/> and
|
||||
/// convert to UTC before the value enters the wire query — otherwise the
|
||||
@@ -547,7 +547,7 @@ public partial class SiteCallsReport
|
||||
return Task.FromResult(_permittedSiteIds.Contains(resolved.Id));
|
||||
}
|
||||
|
||||
// ── Trends (K14) ──────────────────────────────────────────────────────────
|
||||
// ── Trends ──────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Expand/collapse the Trends section. The series load on first expand (and
|
||||
|
||||
@@ -100,7 +100,7 @@ internal static class AlarmTriggerConfigCodec
|
||||
|
||||
case AlarmTriggerType.Expression:
|
||||
model.Expression = TryReadString(root, "expression");
|
||||
// M9-T28b: read optional analysisKind discriminator ("Strict" → strict;
|
||||
// Read optional analysisKind discriminator ("Strict" → strict;
|
||||
// absent/"Advisory"/anything else → Advisory default, preserving
|
||||
// today's behavior exactly — matches ValidationService.IsStrictAnalysis).
|
||||
if (root.TryGetProperty("analysisKind", out var ak)
|
||||
@@ -181,7 +181,7 @@ internal static class AlarmTriggerConfigCodec
|
||||
|
||||
case AlarmTriggerType.Expression:
|
||||
w.WriteString("expression", model.Expression ?? "");
|
||||
// M9-T28b: emit "analysisKind":"Strict" only when explicitly set;
|
||||
// Emit "analysisKind":"Strict" only when explicitly set;
|
||||
// Advisory is the default so the key is omitted to keep the payload
|
||||
// minimal and backward-compatible with older ValidationService versions.
|
||||
if (model.IsStrictAnalysisKind)
|
||||
@@ -357,7 +357,7 @@ internal sealed class AlarmTriggerModel
|
||||
/// </summary>
|
||||
public string? Expression { get; set; }
|
||||
|
||||
// M9-T28b: per-trigger analysis kind (Expression only). When true the
|
||||
// Per-trigger analysis kind (Expression only). When true the
|
||||
// codec serializes "analysisKind":"Strict"; when false (Advisory, the
|
||||
// default) the key is omitted. Matches ValidationService.IsStrictAnalysis.
|
||||
/// <summary>
|
||||
|
||||
@@ -538,7 +538,7 @@
|
||||
_thresholdText = FormatNullable(_model.ThresholdPerSecond);
|
||||
_windowText = FormatNullable(_model.WindowSeconds);
|
||||
_directionText = _model.Direction;
|
||||
// M9-T28b: sync the analysis-kind selector from the loaded model.
|
||||
// Sync the analysis-kind selector from the loaded model.
|
||||
_analysisKindValue = _model.IsStrictAnalysisKind ? "Strict" : "Advisory";
|
||||
_loLoText = FormatNullable(_model.LoLo);
|
||||
_loText = FormatNullable(_model.Lo);
|
||||
@@ -589,7 +589,7 @@
|
||||
<div class="form-text">
|
||||
A boolean C# expression — e.g. <code>Attributes["Temperature"] > 80</code>.
|
||||
</div>
|
||||
@* M9-T28b: analysis-kind selector — Advisory (default) keeps the blank-expression
|
||||
@* Analysis-kind selector — Advisory (default) keeps the blank-expression
|
||||
finding as a non-blocking warning; Strict escalates it to a deploy-blocking error. *@
|
||||
<div class="mt-2">
|
||||
<label for="alarm-trigger-kind" class="form-label small text-uppercase text-muted fw-semibold mb-1">
|
||||
@@ -612,7 +612,7 @@
|
||||
await Emit();
|
||||
}
|
||||
|
||||
// M9-T28b: backing field + handler for the Advisory|Strict selector.
|
||||
// Backing field + handler for the Advisory|Strict selector.
|
||||
private string _analysisKindValue = "Advisory";
|
||||
|
||||
private async Task OnAnalysisKindChanged()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@inject IJSRuntime JS
|
||||
|
||||
@* T34b: dark-mode toggle. Pure JS-interop + localStorage — no DI service and no
|
||||
@* Dark-mode toggle. Pure JS-interop + localStorage — no DI service and no
|
||||
server state. The actual theme switch happens in the browser via window.sbTheme
|
||||
(wwwroot/js/theme.js); this button just drives toggle() and reflects the result.
|
||||
The page-load default (no-flash) is owned by the inline pre-hydration script in
|
||||
|
||||
@@ -25,7 +25,7 @@ public class DialogService : IDialogService
|
||||
/// </summary>
|
||||
public DialogState? Current { get; private set; }
|
||||
|
||||
// CentralUI-015: the pending dialog result is held in a typed TCS that the
|
||||
// The pending dialog result is held in a typed TCS that the
|
||||
// host completes directly via Resolve(). The previous implementation
|
||||
// projected the result through Task.ContinueWith(..., TaskScheduler.Default),
|
||||
// which ran the projection lambda on a thread-pool thread. Completing a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared
|
||||
|
||||
@* M6 (K12) reusable KPI trend chart. Dependency-free inline SVG inside a
|
||||
@* M6 reusable KPI trend chart. Dependency-free inline SVG inside a
|
||||
Bootstrap card, styled to sit beside the AuditKpiTiles / SiteCallKpiTiles
|
||||
KPI cards (card + card-body, small text-muted labels, muted single stroke).
|
||||
Three states: a ≥2-point polyline chart, a single-sample note, and an
|
||||
|
||||
@@ -6,8 +6,8 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Kpi;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Reusable dependency-free SVG trend chart for M6 "KPI History & Trends"
|
||||
/// (Task K12). Renders a <see cref="KpiSeriesPoint"/> series as a single muted
|
||||
/// Reusable dependency-free SVG trend chart for "KPI History & Trends".
|
||||
/// Renders a <see cref="KpiSeriesPoint"/> series as a single muted
|
||||
/// polyline inside a Bootstrap card, with a baseline (x-axis) and value/time
|
||||
/// labels. No third-party charting library — the project rule is custom Blazor
|
||||
/// components on Bootstrap CSS only.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared
|
||||
|
||||
@* T35a — reusable offset-pagination bar. Purely presentational: the host page
|
||||
@* Reusable offset-pagination bar. Purely presentational: the host page
|
||||
owns page-number state, query execution, and the HasNextPage decision.
|
||||
Parameters: Page, PageChanged, HasNextPage, TotalCount, PageSize, Disabled. *@
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Pure helper for windowed pagination (CentralUI-016). Computes the set of
|
||||
/// Pure helper for windowed pagination. Computes the set of
|
||||
/// page numbers a pager should render: always the first and last page plus a
|
||||
/// small range around the current page, with the rest elided. Keeps the
|
||||
/// rendered button count bounded regardless of the total page count, instead
|
||||
|
||||
@@ -43,7 +43,7 @@ internal sealed class ScriptTriggerModel
|
||||
/// <summary>Fire mode (Conditional + Expression). Defaults to <see cref="ScriptTriggerMode.OnTrue"/>.</summary>
|
||||
public ScriptTriggerMode Mode { get; set; } = ScriptTriggerMode.OnTrue;
|
||||
|
||||
// M9-T28b: per-trigger analysis kind (Expression only). When true the
|
||||
// Per-trigger analysis kind (Expression only). When true the
|
||||
// codec serializes "analysisKind":"Strict"; when false (Advisory, the
|
||||
// default) the key is omitted. Matches ValidationService.IsStrictAnalysis.
|
||||
/// <summary>
|
||||
@@ -158,7 +158,7 @@ internal static class ScriptTriggerConfigCodec
|
||||
case ScriptTriggerKind.Expression:
|
||||
model.Expression = root.TryGetProperty("expression", out var e) ? e.GetString() : null;
|
||||
model.Mode = ReadMode(root);
|
||||
// M9-T28b: read optional analysisKind discriminator (matches
|
||||
// Read optional analysisKind discriminator (matches
|
||||
// ValidationService.IsStrictAnalysis — "Strict" case-insensitive → strict;
|
||||
// absent/"Advisory"/anything else → Advisory default).
|
||||
if (root.TryGetProperty("analysisKind", out var ak)
|
||||
@@ -215,7 +215,7 @@ internal static class ScriptTriggerConfigCodec
|
||||
case ScriptTriggerKind.Expression:
|
||||
w.WriteString("expression", model.Expression ?? "");
|
||||
w.WriteString("mode", model.Mode.ToString());
|
||||
// M9-T28b: emit "analysisKind":"Strict" only when explicitly set;
|
||||
// Emit "analysisKind":"Strict" only when explicitly set;
|
||||
// Advisory is the default so the key is omitted to stay backward-compatible.
|
||||
if (model.IsStrictAnalysisKind)
|
||||
w.WriteString("analysisKind", "Strict");
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
_operator = _model.Operator;
|
||||
_thresholdText = _model.Threshold?.ToString("R", CultureInfo.InvariantCulture);
|
||||
(_intervalText, _intervalUnit) = SplitInterval(_model.IntervalMs);
|
||||
// M9-T28b: sync the analysis-kind selector from the loaded model.
|
||||
// Sync the analysis-kind selector from the loaded model.
|
||||
_analysisKindValue = _model.IsStrictAnalysisKind ? "Strict" : "Advisory";
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@
|
||||
<div class="form-text">
|
||||
A boolean C# expression — e.g. <code>Attributes["Temperature"] > 80</code>.
|
||||
</div>
|
||||
@* M9-T28b: analysis-kind selector — Advisory keeps the blank-expression finding
|
||||
@* Analysis-kind selector — Advisory keeps the blank-expression finding
|
||||
as a non-blocking warning; Strict escalates it to a deploy-blocking error. *@
|
||||
<div class="mt-2">
|
||||
<label for="script-trigger-kind" class="form-label small text-uppercase text-muted fw-semibold mb-1">
|
||||
@@ -297,7 +297,7 @@
|
||||
await Emit();
|
||||
}
|
||||
|
||||
// M9-T28b: backing field + handler for the Advisory|Strict selector.
|
||||
// Backing field + handler for the Advisory|Strict selector.
|
||||
private string _analysisKindValue = "Advisory";
|
||||
|
||||
private async Task OnAnalysisKindChanged()
|
||||
|
||||
@@ -34,7 +34,7 @@ public sealed class TemplateTreeNode
|
||||
/// <summary>
|
||||
/// True when this composition node is <em>inherited</em> — its underlying
|
||||
/// <c>TemplateComposition</c> row belongs to an ancestor of the template it is
|
||||
/// rendered under, not to that template itself (followup #9). Inherited nodes are
|
||||
/// rendered under, not to that template itself. Inherited nodes are
|
||||
/// badged read-only and their context menu suppresses Rename/Delete (those edit the
|
||||
/// base). Always false for folders and templates.
|
||||
/// </summary>
|
||||
|
||||
@@ -22,7 +22,7 @@ public class InboundScriptHost
|
||||
|
||||
/// <summary>
|
||||
/// Scoped, parameterized database access. Editor mirror of
|
||||
/// ZB.MOM.WW.ScadaBridge.InboundAPI.InboundDatabaseHelper (InboundAPI-026/030) — its
|
||||
/// ZB.MOM.WW.ScadaBridge.InboundAPI.InboundDatabaseHelper — its
|
||||
/// signatures must match the runtime helper so a script using <c>Database.*</c>
|
||||
/// type-checks during analysis.
|
||||
/// </summary>
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.ScriptAnalysis;
|
||||
/// however, <b>process-global</b>: redirecting it with <c>Console.SetOut</c> for
|
||||
/// the duration of one run corrupts any other run executing concurrently —
|
||||
/// outputs interleave, and whichever run finishes first restores
|
||||
/// <c>Console.Out</c> while the others are still writing (CentralUI-003).
|
||||
/// <c>Console.Out</c> while the others are still writing.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This writer is installed into <c>Console.Out</c>/<c>Console.Error</c>
|
||||
@@ -81,7 +81,7 @@ internal sealed class SandboxConsoleCapture : TextWriter
|
||||
return new CaptureScope(this, previous);
|
||||
}
|
||||
|
||||
// CentralUI-030: intra-script concurrency hardening. A sandboxed script
|
||||
// Intra-script concurrency hardening. A sandboxed script
|
||||
// can fan out work with `Task.WhenAll` / `Task.Run`; `AsyncLocal` flows
|
||||
// the capture `StringWriter` into every child task, so two tasks can
|
||||
// race the *same* buffer. `StringWriter` is not thread-safe — concurrent
|
||||
|
||||
@@ -317,7 +317,7 @@ public class SandboxAttributeAccessor
|
||||
// Batch-write/wait helpers. These mirror the runtime AttributeAccessor
|
||||
// (SiteRuntime/Scripts/ScopeAccessors.cs) and the deploy-gate
|
||||
// ScriptCompileSurface member-for-member so instance scripts using them COMPILE
|
||||
// in the editor and pass Test Run analysis (follow-up #7) — previously the
|
||||
// in the editor and pass Test Run analysis — previously the
|
||||
// sandbox omitted them and the editor false-flagged valid scripts with CS1061.
|
||||
// Execution needs the site's DCL batch path + event-driven attribute waiter,
|
||||
// for which the central Test Run sandbox has no transport, so each throws a
|
||||
|
||||
@@ -224,7 +224,7 @@ public class ScriptAnalysisService
|
||||
SandboxErrorKind.CompileError, 0, markers);
|
||||
}
|
||||
|
||||
// Trust-model gate (CentralUI-001): the documented forbidden-API set is
|
||||
// Trust-model gate: the documented forbidden-API set is
|
||||
// enforced HERE, before execution — not merely surfaced as an editor hint.
|
||||
// Without this, a Design-role user could run arbitrary file/process/
|
||||
// reflection/network code in the central host process.
|
||||
@@ -332,7 +332,7 @@ public class ScriptAnalysisService
|
||||
throw new ScriptSandboxException(
|
||||
$"Scripts.CallShared(\"{name}\") compile failed: {string.Join("; ", nestedErrors.Select(d => d.GetMessage()))}");
|
||||
|
||||
// Trust-model gate (CentralUI-001) — a nested shared script runs
|
||||
// Trust-model gate — a nested shared script runs
|
||||
// arbitrary code too, so it must clear the same forbidden-API gate.
|
||||
if (EnforceTrustModel(built.GetCompilation()).Count > 0)
|
||||
throw new ScriptSandboxException(
|
||||
@@ -384,7 +384,7 @@ public class ScriptAnalysisService
|
||||
Instance = instanceContext,
|
||||
};
|
||||
|
||||
// Console capture is routed per-call via an AsyncLocal scope (CentralUI-003).
|
||||
// Console capture is routed per-call via an AsyncLocal scope.
|
||||
// Console.Out is process-global, so it must NOT be redirected per run — two
|
||||
// concurrent Test Runs would interleave output and the first to finish would
|
||||
// restore Console.Out while the other is still writing. SandboxConsoleCapture
|
||||
@@ -985,7 +985,7 @@ public class ScriptAnalysisService
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the called script's shape from the metadata in scope for its kind.
|
||||
/// CentralUI-013: the shared-script catalog is awaited rather than blocked on
|
||||
/// The shared-script catalog is awaited rather than blocked on
|
||||
/// with <c>.GetAwaiter().GetResult()</c>, so this method is async — and
|
||||
/// <see cref="Hover"/> / <see cref="SignatureHelp"/> are async with it.
|
||||
/// </summary>
|
||||
@@ -1207,7 +1207,7 @@ public class ScriptAnalysisService
|
||||
/// <summary>
|
||||
/// The forbidden namespace/type a symbol implicates, or null if it is allowed.
|
||||
/// Checks the symbol's namespace and — for a type or member — the type's full
|
||||
/// name. M3.5: the allowed-exception carve-out is applied at the WHOLE-SYMBOL
|
||||
/// name. The allowed-exception carve-out is applied at the WHOLE-SYMBOL
|
||||
/// level (mirroring the shared <see cref="SharedTrust.ScriptTrustValidator"/>
|
||||
/// semantic pass), so a type whose full name is an allowed exception
|
||||
/// (e.g. <c>System.Threading.CancellationTokenSource</c>) is NOT flagged even
|
||||
@@ -1233,7 +1233,7 @@ public class ScriptAnalysisService
|
||||
|
||||
/// <summary>
|
||||
/// Fully-qualified names a symbol reference implicates for trust-model checking.
|
||||
/// M3.5: a bare namespace symbol is intentionally ignored — mirroring the shared
|
||||
/// A bare namespace symbol is intentionally ignored — mirroring the shared
|
||||
/// <see cref="SharedTrust.ScriptTrustValidator"/> semantic pass. A namespace name
|
||||
/// on its own performs no action; harm requires referencing a type or member, so
|
||||
/// flagging the bare <c>System.Threading</c> qualifier of an otherwise-allowed
|
||||
@@ -1269,7 +1269,7 @@ public class ScriptAnalysisService
|
||||
: type.Name;
|
||||
|
||||
/// <summary>
|
||||
/// M3.5: the forbidden-namespace verdict for the editor walker is sourced
|
||||
/// The forbidden-namespace verdict for the editor walker is sourced
|
||||
/// from the shared <see cref="SharedTrust.ScriptTrustPolicy"/> — the single
|
||||
/// deny-list the run gate (<see cref="ScriptTrustValidator"/>) also uses, so
|
||||
/// the editor squiggle and the run gate agree on which namespaces are
|
||||
@@ -1290,7 +1290,7 @@ public class ScriptAnalysisService
|
||||
|| actual.StartsWith(root + ".", StringComparison.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Pre-execution trust-model gate (CentralUI-001). M3.5: the VERDICT is
|
||||
/// Pre-execution trust-model gate. The VERDICT is
|
||||
/// delegated to the shared <see cref="SharedTrust.ScriptTrustValidator"/> —
|
||||
/// the single source of truth the editor squiggle deny-list also derives from,
|
||||
/// so the run gate and the editor agree. The shared validator additionally
|
||||
|
||||
@@ -28,7 +28,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddCascadingAuthenticationState();
|
||||
|
||||
// Resolves the current user's permitted site set from their SiteId claims
|
||||
// so Deployment/Monitoring pages can enforce site scoping (CentralUI-002).
|
||||
// so Deployment/Monitoring pages can enforce site scoping.
|
||||
services.AddScoped<SiteScopeService>();
|
||||
|
||||
// Centralised dialog service: pages inject IDialogService and a single
|
||||
@@ -36,7 +36,7 @@ public static class ServiceCollectionExtensions
|
||||
// Components/Shared/IDialogService.cs.
|
||||
services.AddScoped<IDialogService, DialogService>();
|
||||
|
||||
// Audit Log (#23 M7-T3): CentralUI facade over IAuditLogRepository so the
|
||||
// CentralUI facade over IAuditLogRepository so the
|
||||
// results grid can be tested with a stubbed query source.
|
||||
//
|
||||
// Registered with an explicit factory so the IServiceScopeFactory ctor is
|
||||
@@ -49,11 +49,11 @@ public static class ServiceCollectionExtensions
|
||||
sp.GetRequiredService<IServiceScopeFactory>(),
|
||||
sp.GetRequiredService<ICentralHealthAggregator>()));
|
||||
|
||||
// Audit Log (#23 M7-T14 / Bundle F): server-side streaming CSV export.
|
||||
// Server-side streaming CSV export.
|
||||
// Backs the Audit Log page's Export button via GET /api/centralui/audit/export.
|
||||
services.AddScoped<IAuditLogExportService, AuditLogExportService>();
|
||||
|
||||
// KPI History (M6, K11): CentralUI facade over IKpiHistoryRepository that
|
||||
// CentralUI facade over IKpiHistoryRepository that
|
||||
// fetches a raw series and reduces it with KpiSeriesBucketer for the trend chart.
|
||||
//
|
||||
// Registered with an explicit factory so the IServiceScopeFactory ctor is
|
||||
@@ -66,21 +66,21 @@ public static class ServiceCollectionExtensions
|
||||
sp.GetRequiredService<IServiceScopeFactory>(),
|
||||
sp.GetRequiredService<IOptions<KpiHistoryOptions>>()));
|
||||
|
||||
// OPC UA Tag Browser (Task 14): facade over CommunicationService.BrowseNodeAsync
|
||||
// OPC UA Tag Browser: facade over CommunicationService.BrowseNodeAsync
|
||||
// that enforces the CentralUI-side Design-role trust boundary and translates
|
||||
// transport failures into typed BrowseFailure results for the dialog.
|
||||
services.AddScoped<IBrowseService, BrowseService>();
|
||||
|
||||
// Verify Endpoint (M7 T17): facade over CommunicationService.VerifyEndpointAsync
|
||||
// Verify Endpoint: facade over CommunicationService.VerifyEndpointAsync
|
||||
// that enforces the same CentralUI-side Design-role trust boundary as the browse
|
||||
// service, serializes the in-progress endpoint config, and translates transport
|
||||
// failures into typed VerifyEndpointResults. Backs the "Verify endpoint" button
|
||||
// on the OPC UA endpoint editor (read-only connect probe, never trusts certs).
|
||||
services.AddScoped<IEndpointVerificationService, EndpointVerificationService>();
|
||||
|
||||
// OPC UA Cert Management (M7 T17 / D6): facade over the three
|
||||
// OPC UA Cert Management: facade over the three
|
||||
// CommunicationService cert-trust relay methods. Enforces the CentralUI-side
|
||||
// role trust boundary (D7: Trust + Remove require Administrator, List requires
|
||||
// role trust boundary (Trust + Remove require Administrator, List requires
|
||||
// Designer) and translates transport failures into typed CertTrustResults.
|
||||
// Backs the "Trust certificate" button on the OPC UA endpoint editor and the
|
||||
// connection-certificates management page (node-wide site PKI store).
|
||||
@@ -93,7 +93,7 @@ public static class ServiceCollectionExtensions
|
||||
// connection).
|
||||
services.AddScoped<IBindingTester, BindingTester>();
|
||||
|
||||
// Operator Alarm Summary (M7 T13): read-only page that aggregates the
|
||||
// Operator Alarm Summary: read-only page that aggregates the
|
||||
// current alarms across a site's Enabled instances. The service fans out
|
||||
// one debug snapshot per instance via IInstanceSnapshotClient — a thin
|
||||
// facade over CommunicationService.RequestDebugSnapshotAsync (the same
|
||||
@@ -101,7 +101,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<IInstanceSnapshotClient, CommunicationInstanceSnapshotClient>();
|
||||
services.AddScoped<IAlarmSummaryService, AlarmSummaryService>();
|
||||
|
||||
// Secured Writes (M7 T14b): dispatches the two-person secured-write commands
|
||||
// Secured Writes: dispatches the two-person secured-write commands
|
||||
// (submit / approve / reject / list) to the central ManagementActor through the
|
||||
// in-process ManagementActorHolder seam — the same Ask path the HTTP /management
|
||||
// endpoint uses. The server stays the single enforcer of role gating,
|
||||
@@ -109,14 +109,14 @@ public static class ServiceCollectionExtensions
|
||||
// and the append-only audit trail; the page only SUBMITS commands.
|
||||
services.AddScoped<ISecuredWriteService, SecuredWriteService>();
|
||||
|
||||
// Connection live-status (M9-T25): projects the per-site health reports'
|
||||
// Connection live-status: projects the per-site health reports'
|
||||
// name-keyed connection statuses onto a connection-id → ConnectionHealth map
|
||||
// so the design DataConnections page can render a live badge per connection
|
||||
// node. Reuses the existing site→central health transport via
|
||||
// ICentralHealthAggregator — no new plumbing.
|
||||
services.AddScoped<IConnectionHealthQueryService, ConnectionHealthQueryService>();
|
||||
|
||||
// Move-data-connection (M9-T24b): dispatches MoveDataConnectionCommand to the
|
||||
// Move-data-connection: dispatches MoveDataConnectionCommand to the
|
||||
// central ManagementActor through the in-process ManagementActorHolder seam —
|
||||
// the same Ask path the HTTP /management endpoint uses. The server stays the
|
||||
// single enforcer of the Designer gate and every move guard (target exists, no
|
||||
@@ -124,8 +124,8 @@ public static class ServiceCollectionExtensions
|
||||
// the move dialog only SUBMITS the command and renders the returned outcome.
|
||||
services.AddScoped<IDataConnectionMoveService, DataConnectionMoveService>();
|
||||
|
||||
// Schema Library (M9-T32c): authoring + read accessors for the reusable named
|
||||
// JSON-Schema library (SharedSchema + ISharedSchemaRepository, T32a).
|
||||
// Schema Library: authoring + read accessors for the reusable named
|
||||
// JSON-Schema library (SharedSchema + ISharedSchemaRepository).
|
||||
//
|
||||
// ISchemaLibraryService dispatches the CRUD commands to the central
|
||||
// ManagementActor through the in-process ManagementActorHolder seam — the same
|
||||
@@ -134,21 +134,21 @@ public static class ServiceCollectionExtensions
|
||||
// validation; the page only SUBMITS commands.
|
||||
services.AddScoped<ISchemaLibraryService, SchemaLibraryService>();
|
||||
|
||||
// ISchemaLibraryQueryService is the READ counterpart that T30's schema-driven
|
||||
// ISchemaLibraryQueryService is the READ counterpart that the schema-driven
|
||||
// value-entry forms reuse to resolve {"$ref":"lib:Name"} pointers — a name →
|
||||
// schema-JSON map backed by ISharedSchemaRepository over a fresh DI scope per
|
||||
// query (mirroring the AuditLog / KPI query services, off the circuit-scoped
|
||||
// DbContext). Read-only; no mutation goes through it.
|
||||
services.AddScoped<ISchemaLibraryQueryService, SchemaLibraryQueryService>();
|
||||
|
||||
// Template inheritance preview (M9-T26b): a read-only facade that dispatches
|
||||
// Template inheritance preview: a read-only facade that dispatches
|
||||
// GetResolvedTemplateMembersCommand to the central ManagementActor through the
|
||||
// in-process ManagementActorHolder seam (same Ask path as the HTTP /management
|
||||
// endpoint). Powers the template editor's FULL transitively-inherited member set
|
||||
// + base-changed staleness banner; never mutates rows, not on the deploy path.
|
||||
services.AddScoped<ITemplateInheritanceQueryService, TemplateInheritanceQueryService>();
|
||||
|
||||
// SMS Notifications (S7): the NotificationListForm Type selector offers only the
|
||||
// SMS Notifications: the NotificationListForm Type selector offers only the
|
||||
// notification channels that actually have a registered delivery adapter. The
|
||||
// catalog projects the registered INotificationDeliveryAdapter set (Email + SMS,
|
||||
// registered scoped by AddNotificationOutbox into this same container) to its
|
||||
|
||||
@@ -7,7 +7,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Default <see cref="IAlarmSummaryService"/> implementation (M7 T13). Resolves
|
||||
/// Default <see cref="IAlarmSummaryService"/> implementation. Resolves
|
||||
/// the site's Enabled instances, fans out one debug-snapshot fetch per instance
|
||||
/// through the injected <see cref="IInstanceSnapshotClient"/> (capped at eight
|
||||
/// concurrent fetches), and flattens every snapshot's alarm states into rows.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Pure helper for the inbound-API-key re-arch (C3) on the API-method form. The approval
|
||||
/// Pure helper for the inbound-API-key re-arch on the API-method form. The approval
|
||||
/// relationship moved from <c>ApiMethod.ApprovedApiKeyIds</c> (a CSV on the method) onto
|
||||
/// per-key method-scopes managed through <c>IInboundApiKeyAdmin</c>. When an operator edits
|
||||
/// the "Approved API Keys" list for one method, we must reconcile that method's NAME into (or
|
||||
|
||||
@@ -6,14 +6,14 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Flattened, typed view of a canonical <see cref="ZB.MOM.WW.Audit.AuditEvent"/> for the
|
||||
/// Central UI audit pages. C3 (Task 2.5) made the canonical record the seam type — the
|
||||
/// Central UI audit pages. The canonical record is the seam type — the
|
||||
/// query service decomposes it into this view (via <see cref="AuditRowProjection"/>) so the
|
||||
/// existing razor bindings (<c>row.Channel</c>, <c>Event.Status</c>, <c>evt.RequestSummary</c>,
|
||||
/// …) keep working against typed properties rather than parsing <c>DetailsJson</c> inline.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is presentation-only: it carries the same field surface the bespoke
|
||||
/// <c>Commons.Entities.Audit.AuditEvent</c> exposed before C3. <c>ForwardState</c> is always
|
||||
/// <c>Commons.Entities.Audit.AuditEvent</c> exposed previously. <c>ForwardState</c> is always
|
||||
/// null on the central read path (it is site-storage-only and not carried on canonical rows).
|
||||
/// </remarks>
|
||||
public sealed record AuditEventView
|
||||
|
||||
@@ -6,7 +6,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Streaming CSV exporter for the Audit Log page (#23 M7-T14 / Bundle F).
|
||||
/// Streaming CSV exporter for the Audit Log page.
|
||||
///
|
||||
/// <para>
|
||||
/// The exporter iterates <see cref="IAuditLogRepository.QueryAsync"/> page by page
|
||||
@@ -140,7 +140,7 @@ public sealed class AuditLogExportService : IAuditLogExportService
|
||||
var last = page[^1];
|
||||
cursor = new AuditLogPaging(
|
||||
PageSize: pageSize,
|
||||
// C3: canonical OccurredAtUtc is a DateTimeOffset; the keyset
|
||||
// Canonical OccurredAtUtc is a DateTimeOffset; the keyset
|
||||
// cursor column is a UTC DateTime.
|
||||
AfterOccurredAtUtc: last.OccurredAtUtc.UtcDateTime,
|
||||
AfterEventId: last.EventId);
|
||||
|
||||
@@ -9,11 +9,11 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
/// <summary>
|
||||
/// Default <see cref="IAuditLogQueryService"/> implementation — a thin pass-through
|
||||
/// to <see cref="IAuditLogRepository.QueryAsync"/>. Default page size is 100 (the
|
||||
/// AuditResultsGrid default for #23 M7).
|
||||
/// AuditResultsGrid default).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// #23 M7 (Bundle H follow-up): each query opens its OWN DI scope and resolves a
|
||||
/// Each query opens its OWN DI scope and resolves a
|
||||
/// fresh <see cref="IAuditLogRepository"/> — and therefore a fresh
|
||||
/// <c>ScadaBridgeDbContext</c> — rather than sharing the scoped Blazor-circuit
|
||||
/// context. Without this, the Audit Log page's query-string auto-load
|
||||
@@ -31,13 +31,13 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
/// </remarks>
|
||||
public sealed class AuditLogQueryService : IAuditLogQueryService
|
||||
{
|
||||
// M7 Bundle E (T13): trailing window for the Health dashboard's Audit KPI tiles.
|
||||
// Trailing window for the Health dashboard's Audit KPI tiles.
|
||||
// Hard-coded here rather than configurable because the requirement
|
||||
// (Component-AuditLog.md §"Health & KPIs") fixes "rows/min over the last hour"
|
||||
// and "% errors over the last hour" as the KPI definition.
|
||||
private static readonly TimeSpan KpiWindow = TimeSpan.FromHours(1);
|
||||
|
||||
// Audit Log Node filter (Task 15): the distinct-source-nodes lookup powers
|
||||
// Audit Log Node filter: the distinct-source-nodes lookup powers
|
||||
// the filter dropdown population. A 60s cache keeps rendering the filter
|
||||
// bar cheap — node membership changes (failover, scaling) surface within
|
||||
// a minute, which is acceptable for a filter affordance.
|
||||
@@ -100,7 +100,7 @@ public sealed class AuditLogQueryService : IAuditLogQueryService
|
||||
ArgumentNullException.ThrowIfNull(filter);
|
||||
var effective = paging ?? new AuditLogPaging(DefaultPageSize);
|
||||
|
||||
// C3 (Task 2.5): the repository seam returns canonical records; decompose
|
||||
// The repository seam returns canonical records; decompose
|
||||
// each into a flat AuditEventView so the audit pages keep binding to typed
|
||||
// properties.
|
||||
// Test-seam ctor: use the injected repository directly.
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
/// <summary>
|
||||
/// Default <see cref="ICertManagementService"/> implementation — a thin facade over
|
||||
/// the three <see cref="CommunicationService"/> cert-trust relay methods that enforces
|
||||
/// the CentralUI-side role trust boundary (Decision D7: Trust + Remove require
|
||||
/// the CentralUI-side role trust boundary (Trust + Remove require
|
||||
/// <c>Administrator</c>, List requires <c>Designer</c>), and translates transport
|
||||
/// exceptions into a typed <see cref="CertTrustResult"/>.
|
||||
/// </summary>
|
||||
@@ -45,7 +45,7 @@ public sealed class CertManagementService : ICertManagementService
|
||||
string thumbprint,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// D7: trusting a server certificate mutates every site node's PKI store, so
|
||||
// Trusting a server certificate mutates every site node's PKI store, so
|
||||
// it is an Administrator-only action. The site does not enforce envelope-level
|
||||
// roles, so this check must happen here before any cross-cluster traffic.
|
||||
if (!await HasRoleAsync(Roles.Administrator))
|
||||
@@ -81,7 +81,7 @@ public sealed class CertManagementService : ICertManagementService
|
||||
string siteIdentifier,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// D7: listing trusted certs is read-only, so the lower Designer bar applies
|
||||
// Listing trusted certs is read-only, so the lower Designer bar applies
|
||||
// (an Administrator also satisfies this because admins hold every role claim
|
||||
// by convention). Same CentralUI-side guard rationale as TrustAsync.
|
||||
if (!await HasRoleAsync(Roles.Designer))
|
||||
@@ -114,7 +114,7 @@ public sealed class CertManagementService : ICertManagementService
|
||||
string thumbprint,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// D7: removing trust mutates every site node's PKI store, so it is an
|
||||
// Removing trust mutates every site node's PKI store, so it is an
|
||||
// Administrator-only action — same gate as TrustAsync.
|
||||
if (!await HasRoleAsync(Roles.Administrator))
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Read-only operator service that aggregates the current alarm picture across
|
||||
/// all Enabled instances of a single site (M7 T13 — Operator Alarm Summary).
|
||||
/// all Enabled instances of a single site (Operator Alarm Summary).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
|
||||
@@ -4,8 +4,8 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI facade over <see cref="ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories.IAuditLogRepository"/>
|
||||
/// (#23 M7-T3). The Audit Log page's results grid talks to this service rather than
|
||||
/// CentralUI facade over <see cref="ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories.IAuditLogRepository"/>.
|
||||
/// The Audit Log page's results grid talks to this service rather than
|
||||
/// the repository directly so tests can substitute a fake without spinning up EF
|
||||
/// Core, and so a future caching / shaping layer (e.g. server-side CSV streaming)
|
||||
/// can hang off the same seam.
|
||||
@@ -21,7 +21,7 @@ public interface IAuditLogQueryService
|
||||
/// back as the cursor for the next page.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// C3 (Task 2.5): the repository seam returns the canonical
|
||||
/// The repository seam returns the canonical
|
||||
/// <c>ZB.MOM.WW.Audit.AuditEvent</c>; this facade decomposes each row into a flat
|
||||
/// <see cref="AuditEventView"/> so the audit pages keep binding to typed properties.
|
||||
/// </remarks>
|
||||
@@ -38,7 +38,7 @@ public interface IAuditLogQueryService
|
||||
int DefaultPageSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log (#23) M7 Bundle E (T13) — returns the point-in-time KPI snapshot
|
||||
/// Returns the point-in-time KPI snapshot
|
||||
/// the Health dashboard's Audit tiles render. Composes:
|
||||
/// <list type="bullet">
|
||||
/// <item><c>TotalEventsLastHour</c> + <c>ErrorEventsLastHour</c> from
|
||||
@@ -62,7 +62,7 @@ public interface IAuditLogQueryService
|
||||
Task<AuditLogKpiSnapshot> GetKpiSnapshotAsync(CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log ParentExecutionId feature (Task 10) — returns the full
|
||||
/// Audit Log ParentExecutionId feature — returns the full
|
||||
/// execution chain containing <paramref name="executionId"/> as a flat list
|
||||
/// of <see cref="ExecutionTreeNode"/>, delegating to
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories.IAuditLogRepository.GetExecutionTreeAsync"/>.
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI facade over the central-to-site OPC UA server-certificate trust
|
||||
/// commands (T17 / D6). Backs the "Trust certificate" affordance on the OPC UA
|
||||
/// commands. Backs the "Trust certificate" affordance on the OPC UA
|
||||
/// endpoint editor and the dedicated connection-certificates management page: it
|
||||
/// forwards <see cref="TrustServerCertCommand"/> / <see cref="ListServerCertsCommand"/>
|
||||
/// / <see cref="RemoveServerCertCommand"/> to the owning site via
|
||||
@@ -15,7 +15,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
/// The service is the trust boundary for the cert-management capability: site-side
|
||||
/// actors do not unwrap the central trust envelope, so the role check MUST run here
|
||||
/// before any cross-cluster traffic is generated (mirrors <see cref="IBrowseService"/>
|
||||
/// and <see cref="IEndpointVerificationService"/>). Per Decision D7: Trust + Remove
|
||||
/// and <see cref="IEndpointVerificationService"/>). Trust + Remove
|
||||
/// require the <c>Administrator</c> role; List requires the <c>Designer</c> role.
|
||||
/// Transport failures (timeouts, unreachable sites) are translated into a typed
|
||||
/// <see cref="CertTrustResult"/> so callers can render an inline outcome rather than
|
||||
|
||||
@@ -3,7 +3,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// M9-T25: CentralUI facade that projects the per-site health reports' name-keyed
|
||||
/// CentralUI facade that projects the per-site health reports' name-keyed
|
||||
/// connection statuses onto a connection-id → <see cref="ConnectionHealth"/> map for
|
||||
/// the design DataConnections page. Reuses the existing health transport — the
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.HealthMonitoring.ICentralHealthAggregator"/>'s
|
||||
|
||||
@@ -21,7 +21,7 @@ public record DataConnectionMoveResult(bool Success, string? Error)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI facade over the move-data-connection management command (M9-T24b). It
|
||||
/// CentralUI facade over the move-data-connection management command. It
|
||||
/// dispatches the strongly-typed <c>MoveDataConnectionCommand</c> to the central
|
||||
/// <c>ManagementActor</c> through the in-process <c>ManagementActorHolder</c> seam —
|
||||
/// the SAME Ask path the HTTP <c>/management</c> endpoint uses — so the server remains
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
/// <summary>
|
||||
/// CentralUI facade over
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories.IKpiHistoryRepository"/>
|
||||
/// (M6 "KPI History & Trends", K11). The reusable trend chart talks to this
|
||||
/// ("KPI History & Trends"). The reusable trend chart talks to this
|
||||
/// service rather than the repository directly so tests can substitute a fake
|
||||
/// without spinning up EF Core, and so the bucketing / downsampling step lives in
|
||||
/// one place rather than being re-implemented per page.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI read-accessor over the named JSON-Schema library (M9-T32c) for surfaces
|
||||
/// CentralUI read-accessor over the named JSON-Schema library for surfaces
|
||||
/// that need to RESOLVE <c>{"$ref":"lib:Name"}</c> pointers — chiefly the schema-driven
|
||||
/// value-entry forms (T30). It exposes the library as a name → schema-JSON map (and the
|
||||
/// value-entry forms. It exposes the library as a name → schema-JSON map (and the
|
||||
/// equivalent single-name lookup) so the value form can plug the resolved schema text
|
||||
/// into <c>InboundApiSchema.Parse(json, name => query.Resolve(name))</c> exactly the
|
||||
/// way the deploy-time / runtime resolvers do.
|
||||
|
||||
@@ -24,7 +24,7 @@ public record SchemaLibraryActionResult(bool Success, string? Error)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI facade over the schema-library CRUD management commands (M9-T32c). Every
|
||||
/// CentralUI facade over the schema-library CRUD management commands. Every
|
||||
/// mutation (<c>CreateSharedSchemaCommand</c> / <c>UpdateSharedSchemaCommand</c> /
|
||||
/// <c>DeleteSharedSchemaCommand</c>) and the list query is dispatched to the central
|
||||
/// <c>ManagementActor</c> through the in-process <c>ManagementActorHolder</c> seam — the
|
||||
|
||||
@@ -25,8 +25,8 @@ public record SecuredWriteActionResult(bool Success, SecuredWriteDto? Dto, strin
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI facade over the two-person ("secured") write management commands
|
||||
/// (M7 OPC UA / MxGateway UX, Task T14b — page C5). It dispatches the strongly-typed
|
||||
/// CentralUI facade over the two-person ("secured") write management commands.
|
||||
/// It dispatches the strongly-typed
|
||||
/// <c>SubmitSecuredWriteCommand</c> / <c>ApproveSecuredWriteCommand</c> /
|
||||
/// <c>RejectSecuredWriteCommand</c> / <c>ListSecuredWritesCommand</c> to the central
|
||||
/// <c>ManagementActor</c> through the in-process <c>ManagementActorHolder</c> seam —
|
||||
|
||||
@@ -3,8 +3,8 @@ using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Read-only CentralUI facade over the template-inheritance resolve query
|
||||
/// (M9-T26b). Dispatches the <see cref="GetResolvedTemplateMembersCommand"/> to
|
||||
/// Read-only CentralUI facade over the template-inheritance resolve query.
|
||||
/// Dispatches the <see cref="GetResolvedTemplateMembersCommand"/> to
|
||||
/// the central <c>ManagementActor</c> through the in-process
|
||||
/// <c>ManagementActorHolder</c> seam — the SAME Ask path the HTTP
|
||||
/// <c>/management</c> endpoint uses — and returns the freshly-resolved
|
||||
|
||||
@@ -7,7 +7,7 @@ using ZB.MOM.WW.ScadaBridge.KpiHistory;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Default <see cref="IKpiHistoryQueryService"/> implementation (M6 K11) — fetches
|
||||
/// Default <see cref="IKpiHistoryQueryService"/> implementation — fetches
|
||||
/// the raw series via <see cref="IKpiHistoryRepository.GetRawSeriesAsync"/> and
|
||||
/// reduces it with <see cref="KpiSeriesBucketer.Bucket"/> to at most the requested
|
||||
/// (or configured-default) number of points.
|
||||
|
||||
@@ -4,7 +4,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Default <see cref="ISchemaLibraryQueryService"/> implementation (M9-T32c). Reads the
|
||||
/// Default <see cref="ISchemaLibraryQueryService"/> implementation. Reads the
|
||||
/// named JSON-Schema library directly from <see cref="ISharedSchemaRepository"/> over a
|
||||
/// fresh DI scope per query — mirroring <c>AuditLogQueryService</c> /
|
||||
/// <c>KpiHistoryQueryService</c> so a value form's auto-load never races other reads on
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.TemplateEngine/ZB.MOM.WW.ScadaBridge.TemplateEngine.csproj" />
|
||||
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.DeploymentManager/ZB.MOM.WW.ScadaBridge.DeploymentManager.csproj" />
|
||||
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.HealthMonitoring/ZB.MOM.WW.ScadaBridge.HealthMonitoring.csproj" />
|
||||
<!-- SMS Notifications (S7): the NotificationListForm Type selector derives its options
|
||||
<!-- SMS Notifications: the NotificationListForm Type selector derives its options
|
||||
from the registered INotificationDeliveryAdapter set (via INotificationChannelCatalog)
|
||||
so it reflects the channels the central node can actually deliver — never a hardcoded
|
||||
{Email, Sms}. The adapters live in NotificationOutbox; no NuGet packages added. -->
|
||||
@@ -36,7 +36,7 @@
|
||||
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.KpiHistory/ZB.MOM.WW.ScadaBridge.KpiHistory.csproj" />
|
||||
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.Communication/ZB.MOM.WW.ScadaBridge.Communication.csproj" />
|
||||
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.Transport/ZB.MOM.WW.ScadaBridge.Transport.csproj" />
|
||||
<!-- Secured Writes (M7 T14b / C5): the SecuredWriteService dispatches the secured-write
|
||||
<!-- Secured Writes (M7): the SecuredWriteService dispatches the secured-write
|
||||
management commands to the central ManagementActor via the in-process
|
||||
ManagementActorHolder seam (the same Ask path the HTTP /management endpoint uses). -->
|
||||
<ProjectReference Include="../ZB.MOM.WW.ScadaBridge.ManagementService/ZB.MOM.WW.ScadaBridge.ManagementService.csproj" />
|
||||
|
||||
Reference in New Issue
Block a user