docs(src): fix CommentChecker findings across src/ — 1,032 → 5 (all false positives)
Comment/XML-doc changes only, no code logic: - strip project-tracking references (WP*, P*-*, row-NN, #NN, Task N) from comments, keeping the explanatory rationale - add missing summaries, <param>, <returns>, <typeparam> and <inheritdoc/> - replace misused <inheritdoc/> with real docs; where <inheritdoc/> was combined with useful remarks, move the rationale into // comments - drop stale replicationActor <param> on DeploymentManagerActor ctor Remaining 5 are false positives (CWE-1236 x2, failures-1, year-0001, M365). Build 0 errors / 0 warnings.
This commit is contained in:
@@ -292,6 +292,7 @@ public interface IAuditLogRepository
|
||||
/// </remarks>
|
||||
/// <param name="executionId">Any execution id in the chain; the implementation walks to the root and back down.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <param name="permittedSiteIds">Optional site scope restricting which sites' rows are visible; <c>null</c> means unrestricted.</param>
|
||||
/// <returns>A task that resolves to the full execution tree rooted at the topmost ancestor, one node per distinct execution.</returns>
|
||||
Task<IReadOnlyList<ExecutionTreeNode>> GetExecutionTreeAsync(
|
||||
Guid executionId,
|
||||
@@ -307,6 +308,7 @@ public interface IAuditLogRepository
|
||||
/// </summary>
|
||||
/// <param name="executionId">Any execution id in the chain.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <param name="permittedSiteIds">Optional site scope restricting which sites' rows are visible; <c>null</c> means unrestricted.</param>
|
||||
/// <returns>The canonical audit events that make up the flow, oldest first.</returns>
|
||||
Task<IReadOnlyList<AuditEvent>> GetTraceEventsAsync(
|
||||
Guid executionId,
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ public interface INotificationOutboxRepository
|
||||
/// is its own transaction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <b>Why this exists (arch-review 2026-08-19 P5-4).</b> The operator retry/
|
||||
/// <b>Why this exists.</b> The operator retry/
|
||||
/// discard handlers are read-check-write across two round trips:
|
||||
/// <see cref="GetByIdAsync"/> reads the row, the handler checks its
|
||||
/// in-memory <c>Status</c>, then a write persists the transition. A detached
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ public interface ISecuredWriteRepository
|
||||
/// the compare-and-swap guard for the approve-vs-reject race: a stale-read reject
|
||||
/// used to land a blind full-row update while a concurrent approve's multi-second
|
||||
/// site relay was still in flight, marking the row <c>Rejected</c> while the plant
|
||||
/// write physically executed (arch-review 2026-08-19 P6-1). The conditional
|
||||
/// write physically executed. The conditional
|
||||
/// <c>WHERE Status='Pending'</c> makes exactly one of approve/reject/expiry win.
|
||||
/// The loser observes <c>false</c> and must record nothing — no status change and no
|
||||
/// reject audit row.
|
||||
|
||||
@@ -252,6 +252,12 @@ public interface ITemplateEngineRepository
|
||||
Task<IReadOnlyList<Instance>> GetInstancesBySiteIdAsync(int siteId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Reads one bounded, untracked page of topology instance rows for a site/area.</summary>
|
||||
/// <param name="siteId">The site ID.</param>
|
||||
/// <param name="areaId">The area ID to filter by, or null for every area.</param>
|
||||
/// <param name="skip">The number of rows to skip.</param>
|
||||
/// <param name="take">The maximum number of rows to return.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only page of instances.</returns>
|
||||
Task<IReadOnlyList<Instance>> GetTopologyInstancesAsync(int siteId, int? areaId, int skip, int take, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Retrieves an instance by unique name.</summary>
|
||||
|
||||
+3
@@ -4,6 +4,8 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
||||
public interface IConfigurationCommandTransaction
|
||||
{
|
||||
/// <summary>Each database retry supplies a fresh scope; the operation must not perform external side effects.</summary>
|
||||
/// <param name="operation">The database-only operation to run within the transaction.</param>
|
||||
/// <returns>The operation's result, if any.</returns>
|
||||
Task<object?> ExecuteAsync(Func<IServiceProvider, Task<object?>> operation);
|
||||
}
|
||||
|
||||
@@ -11,6 +13,7 @@ public interface IConfigurationCommandTransaction
|
||||
public interface IConfigurationCommitActions
|
||||
{
|
||||
/// <summary>Queues an idempotent local maintenance action.</summary>
|
||||
/// <param name="action">The action to run after a successful commit.</param>
|
||||
void Add(Func<Task> action);
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -2,5 +2,9 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
||||
/// <summary>Private worker handoff for an existing configured SQL connection. Never log or expose the returned secret.</summary>
|
||||
public interface ISandboxDatabaseConnectionSource
|
||||
{
|
||||
/// <summary>Resolves the connection string for a configured connection.</summary>
|
||||
/// <param name="connectionName">The configured database connection name.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The connection string; a secret that must never be logged or exposed.</returns>
|
||||
Task<string> GetSandboxConnectionStringAsync(string connectionName, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public interface ISiteAuditQueue
|
||||
/// <param name="sinceUtc">Lower bound timestamp (UTC).</param>
|
||||
/// <param name="batchSize">Maximum number of rows to return.</param>
|
||||
/// <param name="afterId">
|
||||
/// Composite-keyset tiebreak cursor (WP2.3), mirroring
|
||||
/// Composite-keyset tiebreak cursor, mirroring
|
||||
/// <see cref="IOperationTrackingStore.ReadChangedSinceAsync"/>: when non-null it is the
|
||||
/// <see cref="AuditEvent.EventId"/> ("D" GUID form) of the last row already consumed at
|
||||
/// <paramref name="sinceUtc"/>, and only rows strictly after the composite
|
||||
@@ -125,7 +125,7 @@ public interface ISiteAuditQueue
|
||||
DateTime sinceUtc, int batchSize, string? afterId = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Reconciliation-pull commit surface, cursor form (WP2.3): flips every row at or before
|
||||
/// Reconciliation-pull commit surface, cursor form: flips every row at or before
|
||||
/// the composite cursor <c>(<paramref name="sinceUtc"/>, <paramref name="afterId"/>)</c>
|
||||
/// from <see cref="ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.AuditForwardState.Pending"/>/
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.AuditForwardState.Forwarded"/> to
|
||||
@@ -231,6 +231,12 @@ public interface ISiteAuditQueue
|
||||
/// <c>(OccurredAtUtc, EventId)</c>, at most <paramref name="limit"/> rows. Independent of
|
||||
/// forwarding state — moves no cursor and flips nothing. At most 64 ids per call.
|
||||
/// </summary>
|
||||
/// <param name="executionIds">Execution ids to match against <c>ExecutionId</c> or <c>ParentExecutionId</c>.</param>
|
||||
/// <param name="sinceUtc">Lower bound on <c>OccurredAtUtc</c> (UTC).</param>
|
||||
/// <param name="limit">Maximum number of rows to return.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <param name="afterEventId">Optional <c>EventId</c> keyset cursor for paging past rows already consumed.</param>
|
||||
/// <returns>A task that resolves to the matching audit events ordered by <c>(OccurredAtUtc, EventId)</c>.</returns>
|
||||
Task<IReadOnlyList<AuditEvent>> ReadByExecutionIdsAsync(
|
||||
IReadOnlyCollection<Guid> executionIds, DateTime sinceUtc, int limit, CancellationToken ct = default, string? afterEventId = null);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.DebugView;
|
||||
/// <param name="CorrelationId">Correlation id echoed on the reply.</param>
|
||||
/// <param name="AlarmsOnly">
|
||||
/// When <c>true</c> the site builds ONLY the alarm half of the snapshot and returns an
|
||||
/// empty <see cref="DebugViewSnapshot.AttributeValues"/> list (wire efficiency, WP2.3).
|
||||
/// empty <see cref="DebugViewSnapshot.AttributeValues"/> list (wire efficiency).
|
||||
/// Set by the central per-site live alarm cache, whose seed/reconcile fan-out discards
|
||||
/// every attribute row anyway. Defaults to <c>false</c> so the Debug View and every other
|
||||
/// caller keep the full snapshot; additive on the wire
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Health;
|
||||
|
||||
/// <summary>
|
||||
/// One site node's latest deployed-secrets mirror outcome, as carried on
|
||||
/// <see cref="SiteHealthReport.SecretMirrorStatuses"/> (review finding M1).
|
||||
/// <see cref="SiteHealthReport.SecretMirrorStatuses"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Every site node runs its own <c>DeployedSecretsMirror</c> and records its current
|
||||
|
||||
@@ -75,7 +75,7 @@ public record SiteHealthReport(
|
||||
public double? ScriptOldestBusyAgeSeconds { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// WP3.1: script-execution worker threads the stuck-script watchdog has DETACHED and
|
||||
/// Script-execution worker threads the stuck-script watchdog has DETACHED and
|
||||
/// replaced — a script wedged past its deadline plus grace in uninterruptible blocking
|
||||
/// I/O, so the pool started a fresh thread and the wedged one will exit only when its
|
||||
/// body finally returns. Point-in-time, refreshed by <c>ScriptSchedulerStatsReporter</c>.
|
||||
@@ -85,7 +85,7 @@ public record SiteHealthReport(
|
||||
public int DetachedScriptThreads { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// WP3.1: per-interval count of script and alarm on-trigger runs SHED because
|
||||
/// Per-interval count of script and alarm on-trigger runs SHED because
|
||||
/// <c>MaxConcurrentRunsPerScript</c> runs were already in flight for that script. Raw
|
||||
/// per-interval count (drained on collect) like <see cref="ScriptErrorCount"/>. A
|
||||
/// sustained non-zero value means a trigger is firing faster than its script completes;
|
||||
@@ -136,8 +136,7 @@ public record SiteHealthReport(
|
||||
public long? LocalDbOplogBacklog { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// WP2.6d (arch-review misc — site stream alarm-vs-attribute shared buffer):
|
||||
/// cumulative count of alarm state changes dropped from
|
||||
/// Cumulative count of alarm state changes dropped from
|
||||
/// <c>SiteStreamManager</c>'s dedicated alarm publish queue because it was at
|
||||
/// capacity — i.e. the site-wide alarm hand-off itself fell behind, not a
|
||||
/// per-subscriber buffer. Point-in-time (not reset on collect), refreshed by
|
||||
@@ -154,9 +153,9 @@ public record SiteHealthReport(
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Tri-state, and the distinction is the whole point (deferred-work register row 73).
|
||||
/// Tri-state, and the distinction is the whole point.
|
||||
/// <b>Null = not reported</b>: a central self-report, where the notion does not apply,
|
||||
/// or a site node running a pre-row-73 build. <b><see langword="true"/></b> = the
|
||||
/// or a site node running an older build. <b><see langword="true"/></b> = the
|
||||
/// reconciler is running. <b><see langword="false"/></b> = the node booted WITHOUT it,
|
||||
/// because no <c>IDeploymentConfigFetcher</c> was resolvable.
|
||||
/// </para>
|
||||
@@ -175,12 +174,12 @@ public record SiteHealthReport(
|
||||
public bool? StartupReconcilerActive { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Every site node's latest deployed-secrets mirror outcome (review finding M1) — one
|
||||
/// Every site node's latest deployed-secrets mirror outcome — one
|
||||
/// entry per node that has recorded a row in the replicated <c>secret_mirror_status</c>
|
||||
/// table, including the STANDBY node, whose own health report is never sent.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <b>Null = no data</b>: a central self-report, a pre-M1 site build, a site whose
|
||||
/// <b>Null = no data</b>: a central self-report, an older site build, a site whose
|
||||
/// reporter has not run yet, or a failed read. An EMPTY list means the feature is wired
|
||||
/// but no node has recorded a status yet. A node entry with an empty
|
||||
/// <see cref="SecretMirrorNodeStatus.Failures"/> list is a healthy mirror. Names and
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Instance;
|
||||
/// <param name="Value">Canonical string form of the new value.</param>
|
||||
/// <param name="Timestamp">UTC timestamp of the command.</param>
|
||||
/// <param name="SourceExecutionId">
|
||||
/// Audit Log #23 (ParentExecutionId tag-cascade): the <c>ExecutionId</c> of the
|
||||
/// ParentExecutionId tag-cascade: the <c>ExecutionId</c> of the
|
||||
/// execution issuing this write — a site script run
|
||||
/// (<c>ScriptRuntimeContext.SetAttribute</c>) or the inbound API request behind a
|
||||
/// <c>Route.To(...).SetAttributes(...)</c>. Additive and nullable; <c>null</c>
|
||||
|
||||
@@ -59,7 +59,7 @@ public static class ScadaBridgeTelemetry
|
||||
/// <summary>
|
||||
/// Incremented each time a per-site live-alarm aggregator re-establishes its site-wide
|
||||
/// gRPC stream — a NodeA↔NodeB failover flip or a reconcile-driven reopen after the
|
||||
/// stream was given up (plan #10, Task 6). A sustained climb signals a flapping site link.
|
||||
/// stream was given up. A sustained climb signals a flapping site link.
|
||||
/// </summary>
|
||||
private static readonly Counter<long> _liveAlarmStreamReconnects =
|
||||
Meter.CreateCounter<long>("scadabridge.site.alarm_cache.reconnects", unit: "1",
|
||||
@@ -67,7 +67,7 @@ public static class ScadaBridgeTelemetry
|
||||
|
||||
/// <summary>
|
||||
/// Incremented for each live delta evicted from a per-site live-alarm aggregator's
|
||||
/// bounded pre-seed buffer (drop-oldest, WP2.3). Non-zero means a seed/reconcile
|
||||
/// bounded pre-seed buffer (drop-oldest). Non-zero means a seed/reconcile
|
||||
/// fan-out ran long enough for the delta storm behind it to exceed the cap — the
|
||||
/// dropped transitions are recovered by the fan-out's authoritative snapshot, but a
|
||||
/// sustained climb points at a slow site.
|
||||
@@ -78,7 +78,7 @@ public static class ScadaBridgeTelemetry
|
||||
|
||||
/// <summary>
|
||||
/// Incremented for each debug event evicted from a central debug session's bounded
|
||||
/// pre-snapshot buffer (drop-oldest, WP2.3). The Debug View is lossy-under-backpressure
|
||||
/// pre-snapshot buffer (drop-oldest). The Debug View is lossy-under-backpressure
|
||||
/// by design; this makes the loss measurable instead of unbounded memory growth.
|
||||
/// </summary>
|
||||
private static readonly Counter<long> _debugPreSnapshotDrops =
|
||||
|
||||
@@ -79,7 +79,7 @@ public static class AuditQueryParamParsers
|
||||
/// the filter dimension stays unconstrained.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Register row 99 (closure defect fixed by arch-review 2026-08-19 P6-3). Three
|
||||
/// Register row 99. Three
|
||||
/// traps this closes that a bare
|
||||
/// <see cref="Enum.TryParse{TEnum}(string, bool, out TEnum)"/> does not:
|
||||
/// <list type="number">
|
||||
|
||||
@@ -4,8 +4,15 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
|
||||
/// <summary>Bounded replay data with independently retained chain-root and truncation metadata.</summary>
|
||||
public sealed record TraceEventBatch(Guid RootExecutionId, IReadOnlyList<AuditEvent> Events, bool Truncated) : IReadOnlyList<AuditEvent>
|
||||
{
|
||||
/// <summary>Gets the trace event at the given position.</summary>
|
||||
/// <param name="index">Zero-based position in <see cref="Events"/>.</param>
|
||||
/// <returns>The event at <paramref name="index"/>.</returns>
|
||||
public AuditEvent this[int index] => Events[index];
|
||||
/// <summary>Gets the number of events in the batch.</summary>
|
||||
public int Count => Events.Count;
|
||||
/// <summary>Returns an enumerator over the batch's events.</summary>
|
||||
/// <returns>An enumerator over <see cref="Events"/>.</returns>
|
||||
public IEnumerator<AuditEvent> GetEnumerator() => Events.GetEnumerator();
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ public static class OpcUaReferenceForm
|
||||
/// prefix, or empty/whitespace (nothing to warn about). False only for a bare
|
||||
/// server-namespace index (<c>ns=<n>;…</c> with n ≥ 1).
|
||||
/// </summary>
|
||||
/// <param name="reference">The OPC UA node reference string to classify.</param>
|
||||
/// <returns><see langword="true"/> when the reference is index-durable; otherwise <see langword="false"/>.</returns>
|
||||
public static bool IsDurable(string? reference)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(reference))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Debugging;
|
||||
|
||||
/// <summary>
|
||||
/// Flow Debugger (final review M1): the ONE parser for the header line of a captured
|
||||
/// Flow Debugger: the ONE parser for the header line of a captured
|
||||
/// exception detail. A .NET <c>Exception.ToString()</c> opens with
|
||||
/// <c>Namespace.Type: message</c>, and a wrapper (an <c>AggregateException</c>, a rethrow)
|
||||
/// chains its inner exceptions on that same first line with <c>" ---> "</c>.
|
||||
|
||||
@@ -59,6 +59,10 @@ public static class FlowGraphBuilder
|
||||
/// Builds the graph. <paramref name="truncated"/> and <paramref name="logsTruncated"/> are passed
|
||||
/// through to <see cref="FlowGraph.Truncated"/> / <see cref="FlowGraph.LogsTruncated"/>.
|
||||
/// </summary>
|
||||
/// <param name="events">The audit events to project into the graph.</param>
|
||||
/// <param name="truncated">Whether the event set was truncated.</param>
|
||||
/// <param name="logsTruncated">Whether the log rows were truncated.</param>
|
||||
/// <returns>The built flow graph.</returns>
|
||||
public static FlowGraph Build(IReadOnlyList<AuditEvent> events, bool truncated = false, bool logsTruncated = false)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(events);
|
||||
@@ -338,8 +342,10 @@ public static class FlowGraphBuilder
|
||||
/// A step the script did not wait for: a notification, or a cached operation — any API / DB step whose
|
||||
/// first lifecycle row is not the sync one-shot kind (<c>ApiCall</c> / <c>DbWrite</c>), the same rule the
|
||||
/// projector uses for the <c>sync</c> / <c>cached</c> subtitle. Such steps hang off the chain on an
|
||||
/// <see cref="FlowEdge.Async"/> edge (D4).
|
||||
/// <see cref="FlowEdge.Async"/> edge.
|
||||
/// </summary>
|
||||
/// <param name="step">The step to classify.</param>
|
||||
/// <returns>True when the step is not awaited by the script.</returns>
|
||||
public static bool IsAsync(FlowStep step)
|
||||
{
|
||||
if (step.Kind == FlowStepKind.Notify) return true;
|
||||
@@ -352,9 +358,12 @@ public static class FlowGraphBuilder
|
||||
|
||||
/// <summary>
|
||||
/// Orders the rows of one outbound operation when timestamps tie: submit before attempts before the
|
||||
/// resolve, then non-terminal before terminal statuses — so "first" and "latest" (D3) never fall back
|
||||
/// resolve, then non-terminal before terminal statuses — so "first" and "latest" never fall back
|
||||
/// to GUID order for rows written in the same tick.
|
||||
/// </summary>
|
||||
/// <param name="kind">The audit row kind.</param>
|
||||
/// <param name="status">The audit row status.</param>
|
||||
/// <returns>The rank used to order the rows.</returns>
|
||||
public static int LifecycleRank(string? kind, string? status)
|
||||
{
|
||||
var k = kind switch
|
||||
@@ -444,6 +453,8 @@ public static class FlowGraphBuilder
|
||||
}
|
||||
|
||||
/// <summary>Display rank used to break timestamp ties: spawners before what they spawn.</summary>
|
||||
/// <param name="kind">The step kind.</param>
|
||||
/// <returns>The display rank for the kind.</returns>
|
||||
public static int KindRank(FlowStepKind kind) => kind switch
|
||||
{
|
||||
FlowStepKind.InboundRequest => 0,
|
||||
|
||||
@@ -14,12 +14,18 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Debugging;
|
||||
public static class FlowStepProjector
|
||||
{
|
||||
/// <summary>Projects an audit event onto a flow step.</summary>
|
||||
/// <param name="evt">The audit event.</param>
|
||||
/// <returns>The projected flow step.</returns>
|
||||
public static FlowStep Project(AuditEvent evt)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(evt);
|
||||
return Project(evt, AuditDetailsCodec.Deserialize(evt.DetailsJson));
|
||||
}
|
||||
|
||||
/// <summary>Projects an audit event and its already-deserialized details onto a flow step.</summary>
|
||||
/// <param name="evt">The audit event.</param>
|
||||
/// <param name="d">The deserialized audit details of <paramref name="evt"/>.</param>
|
||||
/// <returns>The projected flow step.</returns>
|
||||
internal static FlowStep Project(AuditEvent evt, AuditDetails d)
|
||||
{
|
||||
var extra = ScriptTraceExtra.TryParse(d.Extra);
|
||||
@@ -52,6 +58,8 @@ public static class FlowStepProjector
|
||||
}
|
||||
|
||||
/// <summary>True for a <c>ScriptTrace.ScriptLog</c> row — a log line, never a step.</summary>
|
||||
/// <param name="evt">The audit event.</param>
|
||||
/// <returns><see langword="true"/> when the event is a script log line.</returns>
|
||||
public static bool IsLogLine(AuditEvent evt)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(evt);
|
||||
@@ -59,6 +67,9 @@ public static class FlowStepProjector
|
||||
return IsLogLine(d);
|
||||
}
|
||||
|
||||
/// <summary>True when the details describe a <c>ScriptTrace.ScriptLog</c> row.</summary>
|
||||
/// <param name="d">The deserialized audit details of the event.</param>
|
||||
/// <returns><see langword="true"/> when the details describe a script log line.</returns>
|
||||
internal static bool IsLogLine(AuditDetails d)
|
||||
{
|
||||
return string.Equals(d.Channel, nameof(AuditChannel.ScriptTrace), StringComparison.Ordinal)
|
||||
@@ -66,12 +77,18 @@ public static class FlowStepProjector
|
||||
}
|
||||
|
||||
/// <summary>Projects a <c>ScriptLog</c> row onto its line. Level/source default to <c>info</c>/<c>script</c> when the extra is missing.</summary>
|
||||
/// <param name="evt">The audit event.</param>
|
||||
/// <returns>The projected log line.</returns>
|
||||
public static FlowLogLine ToLogLine(AuditEvent evt)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(evt);
|
||||
return ToLogLine(evt, AuditDetailsCodec.Deserialize(evt.DetailsJson));
|
||||
}
|
||||
|
||||
/// <summary>Projects a <c>ScriptLog</c> row and its already-deserialized details onto its line.</summary>
|
||||
/// <param name="evt">The audit event.</param>
|
||||
/// <param name="d">The deserialized audit details of <paramref name="evt"/>.</param>
|
||||
/// <returns>The projected log line.</returns>
|
||||
internal static FlowLogLine ToLogLine(AuditEvent evt, AuditDetails d)
|
||||
{
|
||||
var x = ScriptTraceExtra.TryParse(d.Extra);
|
||||
@@ -89,12 +106,18 @@ public static class FlowStepProjector
|
||||
}
|
||||
|
||||
/// <summary>Projects one outbound audit row onto its lifecycle entry (<see cref="FlowStep.Lifecycle"/>).</summary>
|
||||
/// <param name="evt">The audit event.</param>
|
||||
/// <returns>The projected lifecycle row.</returns>
|
||||
public static FlowLifecycleRow ToLifecycleRow(AuditEvent evt)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(evt);
|
||||
return ToLifecycleRow(evt, AuditDetailsCodec.Deserialize(evt.DetailsJson));
|
||||
}
|
||||
|
||||
/// <summary>Projects one outbound audit row and its already-deserialized details onto its lifecycle entry.</summary>
|
||||
/// <param name="evt">The audit event.</param>
|
||||
/// <param name="d">The deserialized audit details of <paramref name="evt"/>.</param>
|
||||
/// <returns>The projected lifecycle row.</returns>
|
||||
internal static FlowLifecycleRow ToLifecycleRow(AuditEvent evt, AuditDetails d)
|
||||
{
|
||||
return new FlowLifecycleRow(
|
||||
@@ -109,6 +132,9 @@ public static class FlowStepProjector
|
||||
}
|
||||
|
||||
/// <summary>Maps a channel/kind string pair onto a step kind.</summary>
|
||||
/// <param name="channel">The audit channel name.</param>
|
||||
/// <param name="kind">The audit kind name.</param>
|
||||
/// <returns>The matching step kind, or <see cref="FlowStepKind.Unknown"/>.</returns>
|
||||
public static FlowStepKind ResolveKind(string? channel, string? kind)
|
||||
{
|
||||
if (string.Equals(channel, nameof(AuditChannel.ScriptTrace), StringComparison.Ordinal))
|
||||
@@ -138,6 +164,9 @@ public static class FlowStepProjector
|
||||
}
|
||||
|
||||
/// <summary>Lane rule: route call / inbound / response → Central; script run / wait → Site; write → Tags; outbound API / DB / notify → External.</summary>
|
||||
/// <param name="kind">The step kind.</param>
|
||||
/// <param name="sourceSiteId">The originating site id, used to place unknown kinds.</param>
|
||||
/// <returns>The lane the step is drawn on.</returns>
|
||||
public static FlowLane ResolveLane(FlowStepKind kind, string? sourceSiteId) => kind switch
|
||||
{
|
||||
FlowStepKind.InboundRequest or FlowStepKind.RouteCall or FlowStepKind.Response => FlowLane.Central,
|
||||
@@ -148,6 +177,8 @@ public static class FlowStepProjector
|
||||
};
|
||||
|
||||
/// <summary>True for the three outbound kinds that live on the External lane.</summary>
|
||||
/// <param name="kind">The step kind.</param>
|
||||
/// <returns><see langword="true"/> for an API call, DB call or notification.</returns>
|
||||
public static bool IsOutbound(FlowStepKind kind)
|
||||
=> kind is FlowStepKind.ApiCall or FlowStepKind.DbCall or FlowStepKind.Notify;
|
||||
|
||||
|
||||
@@ -92,6 +92,9 @@ public sealed class FlowTraceCollector
|
||||
/// the inline copy is filtered through. A null <paramref name="redactor"/> — a host that
|
||||
/// never called <c>AddAuditLog</c>, or a test — collects raw but is still capped.
|
||||
/// </summary>
|
||||
/// <param name="maxEvents">The maximum number of events kept inline.</param>
|
||||
/// <param name="maxBytes">The maximum summed size of the events' details, in bytes.</param>
|
||||
/// <param name="redactor">The redactor applied to the inline copy; null collects raw.</param>
|
||||
public FlowTraceCollector(
|
||||
int maxEvents = DefaultMaxEvents,
|
||||
int maxBytes = DefaultMaxBytes,
|
||||
@@ -142,6 +145,7 @@ public sealed class FlowTraceCollector
|
||||
/// where it builds <c>ScriptCallResult.TraceEvents</c>. Idempotent: a second call returns
|
||||
/// the same contents.
|
||||
/// </summary>
|
||||
/// <returns>The collected events in emission order.</returns>
|
||||
public IReadOnlyList<AuditEvent> Complete()
|
||||
{
|
||||
lock (_gate)
|
||||
@@ -159,6 +163,7 @@ public sealed class FlowTraceCollector
|
||||
/// test-only peek. A reply path must use <see cref="Complete"/>, otherwise an un-awaited call
|
||||
/// finishing after the reply could mutate the list the reply was built from.
|
||||
/// </summary>
|
||||
/// <returns>A copy of the events collected so far.</returns>
|
||||
public IReadOnlyList<AuditEvent> Snapshot()
|
||||
{
|
||||
lock (_gate) return _events.ToArray();
|
||||
|
||||
@@ -68,9 +68,12 @@ public sealed record ScriptTraceExtra
|
||||
};
|
||||
|
||||
/// <summary>Serializes to the compact camelCase JSON stored in <c>AuditDetails.Extra</c>.</summary>
|
||||
/// <returns>The serialized JSON string.</returns>
|
||||
public string ToJson() => JsonSerializer.Serialize(this, JsonOptions);
|
||||
|
||||
/// <summary>Parses <c>AuditDetails.Extra</c>; returns <c>null</c> for null/blank/invalid JSON — never throws.</summary>
|
||||
/// <param name="json">The raw <c>AuditDetails.Extra</c> JSON text.</param>
|
||||
/// <returns>The parsed value, or <c>null</c> when the input is null, blank or invalid.</returns>
|
||||
public static ScriptTraceExtra? TryParse(string? json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json)) return null;
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Which leg of a reset-aware <c>Attributes.WriteBatchAndWaitAsync</c> handshake a
|
||||
/// <see cref="HandshakeResult"/> describes (Gitea #36, design 2026-09-10 §4).
|
||||
/// <see cref="HandshakeResult"/> describes.
|
||||
/// </summary>
|
||||
public enum HandshakePhase
|
||||
{
|
||||
@@ -14,7 +14,7 @@ public enum HandshakePhase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of the reset-aware <c>Attributes.WriteBatchAndWaitAsync</c> overload (Gitea #36):
|
||||
/// Result of the reset-aware <c>Attributes.WriteBatchAndWaitAsync</c> overload:
|
||||
/// like <see cref="WaitResult"/>, plus the <see cref="Phase"/> the outcome belongs to so a
|
||||
/// script can report "timed out waiting for the reset echo" separately from "timed out
|
||||
/// waiting for the device to complete". A reset that is never echoed means the batch and
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Kpi;
|
||||
/// How a KPI metric must be folded when an hour of one-minute
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Entities.Kpi.KpiSample"/> rows is
|
||||
/// aggregated into a single <c>KpiRollupHourly</c> row ("KPI History & Trends"
|
||||
/// hourly rollups, deferred #22). The correct fold differs by metric semantics, and
|
||||
/// hourly rollups). The correct fold differs by metric semantics, and
|
||||
/// choosing the wrong one silently corrupts long-range trend charts.
|
||||
/// </summary>
|
||||
public enum KpiRollupAggregation
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Types.Scripts;
|
||||
public static class ScriptTriggerAttribute
|
||||
{
|
||||
/// <summary>True for the trigger kinds the Flow Debugger fires by WRITING the monitored attribute.</summary>
|
||||
/// <param name="triggerType">The trigger type name.</param>
|
||||
/// <returns>True when the trigger type is a write kind.</returns>
|
||||
public static bool IsWriteKind(string? triggerType) =>
|
||||
string.Equals(triggerType, "ValueChange", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(triggerType, "Conditional", StringComparison.OrdinalIgnoreCase);
|
||||
@@ -20,6 +22,10 @@ public static class ScriptTriggerAttribute
|
||||
/// false (and null) for every other kind, and for a write kind whose config is missing,
|
||||
/// malformed, not an object, or names no attribute.
|
||||
/// </summary>
|
||||
/// <param name="triggerType">The trigger type name.</param>
|
||||
/// <param name="triggerConfigurationJson">The trigger configuration JSON.</param>
|
||||
/// <param name="attribute">Receives the monitored attribute name, or null.</param>
|
||||
/// <returns>True when a monitored attribute was read.</returns>
|
||||
public static bool TryRead(string? triggerType, string? triggerConfigurationJson, out string? attribute)
|
||||
{
|
||||
attribute = null;
|
||||
|
||||
@@ -20,7 +20,7 @@ public static class SecretReferenceScanner
|
||||
/// <summary>
|
||||
/// The single parser for a <c>secret:<name></c> reference value, shared by this scanner
|
||||
/// (what central ships) and every adapter that resolves the reference at connect time (what
|
||||
/// the site looks up), so both sides always agree on the name (final review M3). The value is
|
||||
/// the site looks up), so both sides always agree on the name. The value is
|
||||
/// trimmed, the <c>secret:</c> prefix matches ordinal-ignore-case, and the remainder is
|
||||
/// trimmed; an empty remainder is not a reference. The config-expander form
|
||||
/// <c>${secret:…}</c> is a different syntax and never matches.
|
||||
@@ -45,6 +45,8 @@ public static class SecretReferenceScanner
|
||||
/// string values and returns the distinct names found, ordinal-sorted, each with the ordinal-sorted,
|
||||
/// de-duplicated list of connection names that referenced it.
|
||||
/// </summary>
|
||||
/// <param name="connections">The data connection artifacts to scan.</param>
|
||||
/// <returns>The distinct referenced secrets, each with the connections that reference it.</returns>
|
||||
public static IReadOnlyList<SecretReference> Scan(IEnumerable<DataConnectionArtifact> connections)
|
||||
{
|
||||
var found = new SortedDictionary<string, SortedSet<string>>(StringComparer.Ordinal);
|
||||
|
||||
Reference in New Issue
Block a user