docs: complete XML doc coverage (returns, summaries, inheritdoc)
Resolve all 622 issues flagged by the enhanced CommentChecker: add missing <returns> tags (incl. the standard phrasing on non-generic Task methods), add missing <summary> tags, and replace misused/redundant <inheritdoc/> on members that override or implement nothing with real documentation. Documentation-only — no behavior change; solution builds clean.
This commit is contained in:
@@ -76,7 +76,12 @@ public sealed class AuditLogPartitionMaintenanceService : IHostedService, IDispo
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Starts the background maintenance loop, firing an immediate first tick and then
|
||||
/// repeating every <see cref="AuditLogPartitionMaintenanceOptions.IntervalSeconds"/>.
|
||||
/// </summary>
|
||||
/// <param name="ct">Cancellation token provided by the host.</param>
|
||||
/// <returns>A completed task; the loop runs independently on a background thread.</returns>
|
||||
public Task StartAsync(CancellationToken ct)
|
||||
{
|
||||
// Linked CTS lets StopAsync's cancellation AND the host's shutdown
|
||||
@@ -136,14 +141,21 @@ public sealed class AuditLogPartitionMaintenanceService : IHostedService, IDispo
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Signals the maintenance loop to stop by cancelling its linked token,
|
||||
/// then returns the loop task so the host can await its completion.
|
||||
/// </summary>
|
||||
/// <param name="ct">Cancellation token provided by the host (unused — the internal CTS is cancelled directly).</param>
|
||||
/// <returns>The background loop task, or a completed task if the loop was never started.</returns>
|
||||
public Task StopAsync(CancellationToken ct)
|
||||
{
|
||||
_cts?.Cancel();
|
||||
return _loop ?? Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Disposes the internal <see cref="CancellationTokenSource"/> used to stop the maintenance loop.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_cts?.Dispose();
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface IPullAuditEventsClient
|
||||
/// <param name="sinceUtc">Only events with an <c>OccurredAtUtc</c> at or after this cursor time are returned.</param>
|
||||
/// <param name="batchSize">Maximum number of events to return per call.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the next reconciliation batch with a <c>MoreAvailable</c> flag.</returns>
|
||||
Task<PullAuditEventsResponse> PullAsync(
|
||||
string siteId,
|
||||
DateTime sinceUtc,
|
||||
|
||||
@@ -23,6 +23,7 @@ public interface ISiteEnumerator
|
||||
/// — the actor calls this once per tick.
|
||||
/// </summary>
|
||||
/// <param name="ct">Cancellation token for the async enumeration.</param>
|
||||
/// <returns>A task that resolves to the current set of site entries to poll on the next reconciliation tick.</returns>
|
||||
Task<IReadOnlyList<SiteEntry>> EnumerateAsync(CancellationToken ct = default);
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ public sealed class SiteAuditTelemetryStalledTracker : IDisposable
|
||||
/// Returns a defensive copy of the per-site latched stalled state.
|
||||
/// Absent sites are interpreted as <c>Stalled=false</c> by consumers.
|
||||
/// </summary>
|
||||
/// <returns>A snapshot dictionary mapping each known site ID to its current stalled state.</returns>
|
||||
public IReadOnlyDictionary<string, bool> Snapshot() =>
|
||||
new Dictionary<string, bool>(_state);
|
||||
|
||||
|
||||
@@ -71,6 +71,11 @@ internal static class AuditRedactionPrimitives
|
||||
/// field is over-redacted with <see cref="RedactorErrorMarker"/> and
|
||||
/// <paramref name="onFailure"/> is invoked.
|
||||
/// </summary>
|
||||
/// <param name="json">The raw JSON string to redact; null passes through as null.</param>
|
||||
/// <param name="redactList">Header names (case-insensitive) whose values should be replaced.</param>
|
||||
/// <param name="logger">Logger for warning diagnostics on redactor faults.</param>
|
||||
/// <param name="onFailure">Callback invoked when the redactor stage faults; used to increment health counters.</param>
|
||||
/// <returns>The re-serialized JSON with redacted header values, the original string if nothing was redacted, or <see cref="RedactorErrorMarker"/> on fault.</returns>
|
||||
public static string? RedactHeaders(
|
||||
string? json,
|
||||
IList<string> redactList,
|
||||
@@ -152,6 +157,11 @@ internal static class AuditRedactionPrimitives
|
||||
/// with <see cref="RedactorErrorMarker"/> and <paramref name="onFailure"/>
|
||||
/// is invoked — the user-facing action is never aborted.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to redact; null passes through as null.</param>
|
||||
/// <param name="regexes">Compiled body-redaction regexes applied in order.</param>
|
||||
/// <param name="logger">Logger for warning diagnostics on redactor faults.</param>
|
||||
/// <param name="onFailure">Callback invoked when a regex match faults; used to increment health counters.</param>
|
||||
/// <returns>The value with all regex matches replaced by <see cref="RedactedMarker"/>, or <see cref="RedactorErrorMarker"/> on fault.</returns>
|
||||
public static string? RedactBody(
|
||||
string? value,
|
||||
IReadOnlyList<Regex> regexes,
|
||||
@@ -192,6 +202,11 @@ internal static class AuditRedactionPrimitives
|
||||
/// is over-redacted with <see cref="RedactorErrorMarker"/> and
|
||||
/// <paramref name="onFailure"/> is invoked.
|
||||
/// </summary>
|
||||
/// <param name="json">The raw JSON string to redact; null passes through as null.</param>
|
||||
/// <param name="paramNameRegex">Compiled regex matched against each SQL parameter name.</param>
|
||||
/// <param name="logger">Logger for warning diagnostics on redactor faults.</param>
|
||||
/// <param name="onFailure">Callback invoked when the redactor stage faults; used to increment health counters.</param>
|
||||
/// <returns>The re-serialized JSON with matched parameter values replaced by <see cref="RedactedMarker"/>, the original string if no parameters matched, or <see cref="RedactorErrorMarker"/> on fault.</returns>
|
||||
public static string? RedactSqlParameters(
|
||||
string? json,
|
||||
Regex paramNameRegex,
|
||||
@@ -277,6 +292,10 @@ internal static class AuditRedactionPrimitives
|
||||
/// setting <paramref name="truncated"/> to <c>true</c> when the value was
|
||||
/// shortened. Null passes through as null.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to truncate; null passes through as null.</param>
|
||||
/// <param name="cap">Maximum number of UTF-8 bytes to retain.</param>
|
||||
/// <param name="truncated">Set to <c>true</c> when the value was shortened; unchanged otherwise.</param>
|
||||
/// <returns>The truncated string, the original string if within the cap, or <c>null</c> if the input was null.</returns>
|
||||
public static string? TruncateField(string? value, int cap, ref bool truncated)
|
||||
{
|
||||
if (value is null)
|
||||
@@ -299,6 +318,9 @@ internal static class AuditRedactionPrimitives
|
||||
/// (<c>byte & 0xC0 == 0x80</c>), and decodes the resulting prefix —
|
||||
/// guaranteeing the returned string never splits a multi-byte sequence.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to truncate.</param>
|
||||
/// <param name="capBytes">Maximum number of UTF-8 bytes in the returned string.</param>
|
||||
/// <returns>The truncated string guaranteed not to split a multi-byte UTF-8 sequence, or the original string if within the cap.</returns>
|
||||
public static string TruncateUtf8(string value, int capBytes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
|
||||
@@ -35,6 +35,8 @@ internal sealed class AuditRegexCache
|
||||
private readonly ConcurrentDictionary<string, CompiledRegex> _cache = new();
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>Initializes the cache with the logger used to report compile failures.</summary>
|
||||
/// <param name="logger">Logger for recording invalid or slow-compile pattern warnings.</param>
|
||||
public AuditRegexCache(ILogger logger) => _logger = logger;
|
||||
|
||||
/// <summary>
|
||||
@@ -44,6 +46,9 @@ internal sealed class AuditRegexCache
|
||||
/// compile time "invalid"); the failure is logged once and the sentinel
|
||||
/// cache entry prevents repeat compile attempts.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The regex pattern string to look up or compile.</param>
|
||||
/// <param name="regex">The compiled <see cref="Regex"/>, or <c>null</c> if the pattern is invalid.</param>
|
||||
/// <returns><c>true</c> if the pattern compiled successfully; <c>false</c> if it is invalid or too slow to compile.</returns>
|
||||
public bool TryGet(string pattern, out Regex? regex)
|
||||
{
|
||||
var entry = _cache.GetOrAdd(pattern, Compile);
|
||||
@@ -88,8 +93,11 @@ internal sealed class AuditRegexCache
|
||||
{
|
||||
public static readonly CompiledRegex Invalid = new(null);
|
||||
|
||||
/// <summary>The compiled regex, or <c>null</c> when this entry represents an invalid pattern.</summary>
|
||||
public Regex? Regex { get; }
|
||||
|
||||
/// <summary>Initializes the entry with the compiled regex (or <c>null</c> for the invalid sentinel).</summary>
|
||||
/// <param name="regex">The compiled <see cref="Regex"/>, or <c>null</c> for a failed compile.</param>
|
||||
public CompiledRegex(Regex? regex) => Regex = regex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,15 @@ public sealed class SafeDefaultAuditRedactor : IAuditRedactor
|
||||
|
||||
private SafeDefaultAuditRedactor() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Applies line-oriented header redaction to the default sensitive headers
|
||||
/// (<c>Authorization</c>, <c>X-Api-Key</c>, <c>Cookie</c>, <c>Set-Cookie</c>)
|
||||
/// found in <c>RequestSummary</c> and <c>ResponseSummary</c> inside
|
||||
/// <paramref name="rawEvent"/>.<c>DetailsJson</c>. Never throws; over-redacts on
|
||||
/// any internal failure.
|
||||
/// </summary>
|
||||
/// <param name="rawEvent">The audit event whose details JSON is to be redacted.</param>
|
||||
/// <returns>A new <see cref="AuditEvent"/> with sensitive headers replaced by the redacted marker, or an over-redacted sentinel on failure.</returns>
|
||||
public AuditEvent Apply(AuditEvent rawEvent)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(rawEvent);
|
||||
|
||||
@@ -73,7 +73,12 @@ public sealed class ScadaBridgeAuditRedactor : IAuditRedactor
|
||||
_regexCache = new AuditRegexCache(_logger);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Applies the full redaction pipeline to <paramref name="rawEvent"/> and returns a
|
||||
/// filtered copy; returns the same instance unchanged on the fast path. Never throws.
|
||||
/// </summary>
|
||||
/// <param name="rawEvent">The raw audit event to redact.</param>
|
||||
/// <returns>A redacted copy of <paramref name="rawEvent"/>, or the original instance when no changes are needed.</returns>
|
||||
public AuditEvent Apply(AuditEvent rawEvent)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -96,6 +96,7 @@ public sealed class RingBufferFallback
|
||||
/// must call <see cref="Complete"/> first.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token to abort the async enumeration.</param>
|
||||
/// <returns>An async sequence of buffered <see cref="AuditEvent"/> values in FIFO order.</returns>
|
||||
public async IAsyncEnumerable<AuditEvent> DrainAsync(
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -69,7 +69,9 @@ public sealed class SiteAuditBacklogReporter : IHostedService, IDisposable
|
||||
_refreshInterval = refreshInterval ?? DefaultRefreshInterval;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Starts the background polling loop, running an immediate first probe before entering the timed cycle.</summary>
|
||||
/// <param name="ct">Cancellation token signalling host shutdown.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public Task StartAsync(CancellationToken ct)
|
||||
{
|
||||
// Linked CTS lets StopAsync's cancellation AND the host's shutdown
|
||||
@@ -123,14 +125,16 @@ public sealed class SiteAuditBacklogReporter : IHostedService, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Signals the polling loop to stop and waits for it to complete.</summary>
|
||||
/// <param name="ct">Cancellation token (not used; the internal CTS governs shutdown).</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public Task StopAsync(CancellationToken ct)
|
||||
{
|
||||
_cts?.Cancel();
|
||||
return _loop ?? Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Releases the internal <see cref="CancellationTokenSource"/> used to stop the polling loop.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_cts?.Dispose();
|
||||
|
||||
@@ -244,7 +244,13 @@ public class SqliteAuditWriter : IAuditWriter, ISiteAuditQueue, IAsyncDisposable
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Enqueues an audit event for asynchronous batched persistence to SQLite.
|
||||
/// Back-pressure is applied when the write channel is full.
|
||||
/// </summary>
|
||||
/// <param name="evt">The audit event to persist.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that completes when the event has been persisted.</returns>
|
||||
public Task WriteAsync(AuditEvent evt, CancellationToken ct = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(evt);
|
||||
@@ -469,7 +475,13 @@ public class SqliteAuditWriter : IAuditWriter, ISiteAuditQueue, IAsyncDisposable
|
||||
return CachedTelemetryKinds.Contains(kind);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Returns up to <paramref name="limit"/> non-cached pending audit events, oldest first.
|
||||
/// Cached-lifecycle kinds are excluded; use <see cref="ReadPendingCachedTelemetryAsync"/> for those.
|
||||
/// </summary>
|
||||
/// <param name="limit">Maximum number of rows to return.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of pending audit events.</returns>
|
||||
public Task<IReadOnlyList<AuditEvent>> ReadPendingAsync(int limit, CancellationToken ct = default)
|
||||
{
|
||||
if (limit <= 0)
|
||||
@@ -512,7 +524,13 @@ public class SqliteAuditWriter : IAuditWriter, ISiteAuditQueue, IAsyncDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Returns up to <paramref name="limit"/> pending cached-lifecycle audit events, oldest first.
|
||||
/// Only rows with cached-call kinds (CachedSubmit, ApiCallCached, DbWriteCached, CachedResolve) are included.
|
||||
/// </summary>
|
||||
/// <param name="limit">Maximum number of rows to return.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of pending cached-telemetry audit events.</returns>
|
||||
public Task<IReadOnlyList<AuditEvent>> ReadPendingCachedTelemetryAsync(
|
||||
int limit, CancellationToken ct = default)
|
||||
{
|
||||
@@ -560,6 +578,7 @@ public class SqliteAuditWriter : IAuditWriter, ISiteAuditQueue, IAsyncDisposable
|
||||
/// </summary>
|
||||
/// <param name="limit">Maximum number of rows to return.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of forwarded audit events.</returns>
|
||||
public Task<IReadOnlyList<AuditEvent>> ReadForwardedAsync(int limit, CancellationToken ct = default)
|
||||
{
|
||||
if (limit <= 0)
|
||||
@@ -645,7 +664,15 @@ public class SqliteAuditWriter : IAuditWriter, ISiteAuditQueue, IAsyncDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Returns up to <paramref name="batchSize"/> pending or forwarded audit events
|
||||
/// with <see cref="AuditEvent.OccurredAtUtc"/> >= <paramref name="sinceUtc"/>, oldest first.
|
||||
/// Used by the M6 reconciliation-pull handler.
|
||||
/// </summary>
|
||||
/// <param name="sinceUtc">Lower bound timestamp (UTC) for event occurrence.</param>
|
||||
/// <param name="batchSize">Maximum number of rows to return.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of audit events since the given timestamp.</returns>
|
||||
public Task<IReadOnlyList<AuditEvent>> ReadPendingSinceAsync(
|
||||
DateTime sinceUtc, int batchSize, CancellationToken ct = default)
|
||||
{
|
||||
@@ -867,6 +894,7 @@ public class SqliteAuditWriter : IAuditWriter, ISiteAuditQueue, IAsyncDisposable
|
||||
}
|
||||
|
||||
/// <summary>Asynchronously disposes the audit writer and releases resources.</summary>
|
||||
/// <returns>A <see cref="ValueTask"/> that completes when all resources have been released.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
Task? writerLoop;
|
||||
|
||||
@@ -44,6 +44,9 @@ public sealed class ClusterClientSiteAuditClient : ISiteStreamAuditClient
|
||||
private readonly IActorRef _siteCommunicationActor;
|
||||
private readonly TimeSpan _askTimeout;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance that forwards audit telemetry to central via the site's <c>SiteCommunicationActor</c>.
|
||||
/// </summary>
|
||||
/// <param name="siteCommunicationActor">
|
||||
/// The site's <c>SiteCommunicationActor</c> — it forwards the ingest command
|
||||
/// over the registered central ClusterClient and routes the reply back to
|
||||
|
||||
@@ -22,6 +22,7 @@ public interface ISiteStreamAuditClient
|
||||
/// </summary>
|
||||
/// <param name="batch">The batch of audit events to forward.</param>
|
||||
/// <param name="ct">Cancellation token for the operation.</param>
|
||||
/// <returns>A task that resolves to the ingest acknowledgement containing accepted event IDs.</returns>
|
||||
Task<IngestAck> IngestAuditEventsAsync(AuditEventBatch batch, CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
@@ -42,5 +43,6 @@ public interface ISiteStreamAuditClient
|
||||
/// </remarks>
|
||||
/// <param name="batch">The batch of cached-call telemetry packets to forward.</param>
|
||||
/// <param name="ct">Cancellation token for the operation.</param>
|
||||
/// <returns>A task that resolves to the ingest acknowledgement containing accepted event IDs.</returns>
|
||||
Task<IngestAck> IngestCachedTelemetryAsync(CachedTelemetryBatch batch, CancellationToken ct);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ public static class ApiMethodCommands
|
||||
/// <param name="formatOption">Global option for the output format.</param>
|
||||
/// <param name="usernameOption">Global option for the authentication username.</param>
|
||||
/// <param name="passwordOption">Global option for the authentication password.</param>
|
||||
/// <returns>The configured <c>api-method</c> command with all subcommands registered.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("api-method") { Description = "Manage inbound API methods" };
|
||||
|
||||
@@ -18,6 +18,7 @@ public static class AuditCommands
|
||||
/// <param name="formatOption">Global <c>--format</c> option for output format.</param>
|
||||
/// <param name="usernameOption">Global <c>--username</c> option for authentication.</param>
|
||||
/// <param name="passwordOption">Global <c>--password</c> option for authentication.</param>
|
||||
/// <returns>The configured <c>audit</c> <see cref="Command"/> with all sub-commands attached.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("audit") { Description = "Query and export the centralized audit log" };
|
||||
|
||||
@@ -74,6 +74,7 @@ public static class AuditExportHelpers
|
||||
/// </summary>
|
||||
/// <param name="args">The export arguments containing filters and format.</param>
|
||||
/// <param name="now">The current time for resolving relative time specifications.</param>
|
||||
/// <returns>The full query string (including the leading <c>?</c>) for the export endpoint.</returns>
|
||||
public static string BuildQueryString(AuditExportArgs args, DateTimeOffset now)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
@@ -116,6 +117,7 @@ public static class AuditExportHelpers
|
||||
/// <param name="args">The export arguments containing filters and output file path.</param>
|
||||
/// <param name="output">Text writer for command output messages.</param>
|
||||
/// <param name="now">The current time for resolving relative time specifications.</param>
|
||||
/// <returns>0 on success, 1 on general error, or 2 on authorization failure.</returns>
|
||||
public static async Task<int> RunExportAsync(
|
||||
ManagementHttpClient client, AuditExportArgs args, TextWriter output, DateTimeOffset now)
|
||||
{
|
||||
@@ -178,6 +180,8 @@ public static class AuditExportHelpers
|
||||
/// to extract the <c>code</c> field. Returns null if the body is empty, not valid JSON, or
|
||||
/// has no <c>code</c> property — callers fall back to "ERROR" in that case.
|
||||
/// </summary>
|
||||
/// <param name="body">The HTTP response body string to parse for an error code.</param>
|
||||
/// <returns>The <c>code</c> string from the JSON error envelope, or null if absent or unparseable.</returns>
|
||||
internal static string? TryExtractErrorCode(string body)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(body))
|
||||
|
||||
@@ -43,6 +43,7 @@ public static class AuditFormatterFactory
|
||||
/// </summary>
|
||||
/// <param name="format">Format name; <c>table</c> selects the table formatter, any other value selects JSONL.</param>
|
||||
/// <param name="notices">Writer for notice messages emitted during formatting.</param>
|
||||
/// <returns>The <see cref="IAuditFormatter"/> appropriate for the requested format.</returns>
|
||||
public static IAuditFormatter Create(string format, TextWriter notices)
|
||||
{
|
||||
if (string.Equals(format, "table", StringComparison.OrdinalIgnoreCase))
|
||||
|
||||
@@ -50,6 +50,7 @@ public static class AuditLogCommands
|
||||
/// <param name="formatOption">Global output format option.</param>
|
||||
/// <param name="usernameOption">Global username option.</param>
|
||||
/// <param name="passwordOption">Global password option.</param>
|
||||
/// <returns>The configured <c>audit-config</c> command with all sub-commands registered.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("audit-config") { Description = "Query the configuration-change audit log" };
|
||||
|
||||
@@ -61,6 +61,7 @@ public static class AuditQueryHelpers
|
||||
/// <param name="spec">The time specification string.</param>
|
||||
/// <param name="now">The current time used as reference for relative specs.</param>
|
||||
/// <exception cref="FormatException">The spec is neither a known relative form nor a parseable ISO-8601 timestamp.</exception>
|
||||
/// <returns>The resolved absolute <see cref="DateTimeOffset"/> in UTC.</returns>
|
||||
public static DateTimeOffset ResolveTimeSpec(string spec, DateTimeOffset now)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(spec))
|
||||
@@ -103,6 +104,7 @@ public static class AuditQueryHelpers
|
||||
/// <param name="now">The current time for resolving relative time specs.</param>
|
||||
/// <param name="afterOccurredAtUtc">Optional keyset cursor timestamp.</param>
|
||||
/// <param name="afterEventId">Optional keyset cursor event ID.</param>
|
||||
/// <returns>A URL query string (starting with <c>?</c>) containing the encoded filter parameters, or an empty string if no parameters are set.</returns>
|
||||
public static string BuildQueryString(
|
||||
AuditQueryArgs args, DateTimeOffset now, DateTimeOffset? afterOccurredAtUtc, string? afterEventId)
|
||||
{
|
||||
@@ -169,6 +171,7 @@ public static class AuditQueryHelpers
|
||||
/// <param name="formatter">The audit result formatter.</param>
|
||||
/// <param name="output">The output writer for results.</param>
|
||||
/// <param name="now">The current time for resolving relative time specs.</param>
|
||||
/// <returns>A task that resolves to <c>0</c> on success, <c>1</c> on HTTP/transport error, or <c>2</c> on authorization failure.</returns>
|
||||
public static async Task<int> RunQueryAsync(
|
||||
ManagementHttpClient client,
|
||||
AuditQueryArgs args,
|
||||
|
||||
@@ -14,6 +14,7 @@ public static class AuditVerifyChainHelpers
|
||||
/// with a real month (01-12). A malformed month (e.g. <c>2026-13</c>) is rejected.
|
||||
/// </summary>
|
||||
/// <param name="month">The month string to validate in YYYY-MM format.</param>
|
||||
/// <returns><c>true</c> if the string is a well-formed YYYY-MM value with a real month; otherwise <c>false</c>.</returns>
|
||||
public static bool IsValidMonth(string? month)
|
||||
=> !string.IsNullOrWhiteSpace(month)
|
||||
&& DateTime.TryParseExact(month, "yyyy-MM", CultureInfo.InvariantCulture,
|
||||
|
||||
@@ -307,6 +307,13 @@ public static class BundleCommands
|
||||
// for the post-write summary line.
|
||||
internal const int Base64StreamChunkChars = 1024 * 1024; // 1 MB of base64 chars ≈ 768 KB decoded
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a base64 string into <paramref name="outputPath"/> in chunked fashion to avoid
|
||||
/// large intermediate allocations. Returns the total number of decoded bytes written.
|
||||
/// </summary>
|
||||
/// <param name="base64">The base64-encoded content to decode and write.</param>
|
||||
/// <param name="outputPath">Destination file path; created or overwritten.</param>
|
||||
/// <returns>Total number of bytes written to the output file.</returns>
|
||||
internal static long StreamBase64ToFile(string base64, string outputPath)
|
||||
{
|
||||
if (base64 is null) throw new ArgumentNullException(nameof(base64));
|
||||
|
||||
@@ -17,6 +17,7 @@ internal static class CliOptions
|
||||
/// typo (e.g. <c>--format tabel</c>) is rejected with a clear parse error rather
|
||||
/// than silently falling through to JSON.
|
||||
/// </summary>
|
||||
/// <returns>The configured <c>--format</c> option constrained to "json" or "table".</returns>
|
||||
internal static Option<string> CreateFormatOption()
|
||||
{
|
||||
var formatOption = new Option<string>("--format")
|
||||
|
||||
@@ -30,6 +30,7 @@ internal static class CommandHelpers
|
||||
/// (<see cref="IsAuthorizationFailure"/>) is preserved on the error path either way,
|
||||
/// closing CLI-017's regression.
|
||||
/// </param>
|
||||
/// <returns>A task that resolves to the process exit code (0 = success, 1 = error, 2 = authorization failure).</returns>
|
||||
internal static async Task<int> ExecuteCommandAsync(
|
||||
ParseResult result,
|
||||
Option<string> urlOption,
|
||||
@@ -110,6 +111,7 @@ internal static class CommandHelpers
|
||||
/// <param name="result">Parsed command-line result.</param>
|
||||
/// <param name="formatOption">The <c>--format</c> option definition.</param>
|
||||
/// <param name="config">Loaded CLI configuration providing the default format fallback.</param>
|
||||
/// <returns>The resolved format string (e.g. <c>"json"</c> or <c>"table"</c>).</returns>
|
||||
internal static string ResolveFormat(ParseResult result, Option<string> formatOption, CliConfig config)
|
||||
{
|
||||
// GetResult returns non-null only when the option was actually present on the
|
||||
@@ -130,6 +132,7 @@ internal static class CommandHelpers
|
||||
/// </summary>
|
||||
/// <param name="commandLineValue">Value supplied on the command line, or null if absent.</param>
|
||||
/// <param name="envValue">Fallback value from the config file or environment variable.</param>
|
||||
/// <returns>The command-line value when non-empty; otherwise the environment fallback (may be null).</returns>
|
||||
internal static string? ResolveCredential(string? commandLineValue, string? envValue)
|
||||
=> string.IsNullOrWhiteSpace(commandLineValue) ? envValue : commandLineValue;
|
||||
|
||||
@@ -140,6 +143,7 @@ internal static class CommandHelpers
|
||||
/// an unhandled <see cref="UriFormatException"/>.
|
||||
/// </summary>
|
||||
/// <param name="url">URL string to validate.</param>
|
||||
/// <returns><c>true</c> when the URL is an absolute http or https URL; otherwise <c>false</c>.</returns>
|
||||
internal static bool IsValidManagementUrl(string? url)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
@@ -154,6 +158,7 @@ internal static class CommandHelpers
|
||||
/// </summary>
|
||||
/// <param name="response">Response received from the management API.</param>
|
||||
/// <param name="format">Output format (<c>json</c> or <c>table</c>).</param>
|
||||
/// <returns>The process exit code (0 = success, 1 = error, 2 = authorization failure).</returns>
|
||||
internal static int HandleResponse(ManagementResponse response, string format)
|
||||
{
|
||||
if (response.JsonData != null)
|
||||
@@ -192,6 +197,8 @@ internal static class CommandHelpers
|
||||
/// both channels are honoured. (Authentication failure — HTTP 401 / bad credentials
|
||||
/// — is deliberately <em>not</em> treated as authorization failure; it is exit 1.)
|
||||
/// </summary>
|
||||
/// <param name="response">The management response to inspect for authorization failure signals.</param>
|
||||
/// <returns><c>true</c> when the response signals an authorization failure (HTTP 403 or FORBIDDEN/UNAUTHORIZED code).</returns>
|
||||
internal static bool IsAuthorizationFailure(ManagementResponse response)
|
||||
{
|
||||
if (response.StatusCode == 403)
|
||||
|
||||
@@ -13,6 +13,7 @@ public static class DataConnectionCommands
|
||||
/// <param name="formatOption">Global output format option.</param>
|
||||
/// <param name="usernameOption">Global username option.</param>
|
||||
/// <param name="passwordOption">Global password option.</param>
|
||||
/// <returns>The configured <c>data-connection</c> <see cref="Command"/> with all subcommands registered.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("data-connection") { Description = "Manage data connections" };
|
||||
|
||||
@@ -15,6 +15,7 @@ public static class DebugCommands
|
||||
/// <param name="formatOption">Shared output format option.</param>
|
||||
/// <param name="usernameOption">Shared username option for authentication.</param>
|
||||
/// <param name="passwordOption">Shared password option for authentication.</param>
|
||||
/// <returns>The configured <c>debug</c> command with snapshot and stream subcommands registered.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("debug") { Description = "Runtime debugging" };
|
||||
|
||||
@@ -27,6 +27,7 @@ internal static class DebugStreamHelpers
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception thrown by HubConnection.StartAsync.</param>
|
||||
/// <param name="cancellationRequested">True when the user requested cancellation (Ctrl+C) before the exception was thrown.</param>
|
||||
/// <returns>A <see cref="ConnectFailure"/> describing whether the failure was a cancellation and the appropriate exit code.</returns>
|
||||
internal static ConnectFailure ClassifyConnectFailure(Exception ex, bool cancellationRequested)
|
||||
{
|
||||
if (cancellationRequested && ex is OperationCanceledException)
|
||||
@@ -43,6 +44,7 @@ internal static class DebugStreamHelpers
|
||||
/// result is ever produced (pure Ctrl+C), the stream ended gracefully — exit 0.
|
||||
/// </summary>
|
||||
/// <param name="exitTask">The task whose result is the intended exit code, set by OnStreamTerminated or the Closed handler.</param>
|
||||
/// <returns>A task that resolves to the process exit code (0 for graceful exit or pure Ctrl+C, non-zero for error).</returns>
|
||||
internal static async Task<int> ResolveStreamExitCodeAsync(Task<int> exitTask)
|
||||
{
|
||||
if (exitTask.IsCompletedSuccessfully)
|
||||
|
||||
@@ -13,6 +13,7 @@ public static class DeployCommands
|
||||
/// <param name="formatOption">Global output format option.</param>
|
||||
/// <param name="usernameOption">Global username option.</param>
|
||||
/// <param name="passwordOption">Global password option.</param>
|
||||
/// <returns>The configured <c>deploy</c> <see cref="Command"/> with all sub-commands attached.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("deploy") { Description = "Deployment operations" };
|
||||
|
||||
@@ -13,6 +13,7 @@ public static class ExternalSystemCommands
|
||||
/// <param name="formatOption">Global option for the output format.</param>
|
||||
/// <param name="usernameOption">Global option for the authentication username.</param>
|
||||
/// <param name="passwordOption">Global option for the authentication password.</param>
|
||||
/// <returns>The fully configured <c>external-system</c> <see cref="Command"/> with all subcommands registered.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("external-system") { Description = "Manage external systems" };
|
||||
|
||||
@@ -13,6 +13,7 @@ public static class HealthCommands
|
||||
/// <param name="formatOption">Global <c>--format</c> option for output format.</param>
|
||||
/// <param name="usernameOption">Global <c>--username</c> option for authentication.</param>
|
||||
/// <param name="passwordOption">Global <c>--password</c> option for authentication.</param>
|
||||
/// <returns>The configured <c>health</c> command with all sub-commands registered.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("health") { Description = "Health monitoring" };
|
||||
|
||||
@@ -13,6 +13,7 @@ public static class NotificationCommands
|
||||
/// <param name="formatOption">Global <c>--format</c> option for output format.</param>
|
||||
/// <param name="usernameOption">Global <c>--username</c> option for authentication.</param>
|
||||
/// <param name="passwordOption">Global <c>--password</c> option for authentication.</param>
|
||||
/// <returns>The configured <c>notification</c> command with all sub-commands registered.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("notification") { Description = "Manage notification lists" };
|
||||
@@ -131,6 +132,7 @@ public static class NotificationCommands
|
||||
/// null when omitted so the server-side handler preserves the existing values.
|
||||
/// </summary>
|
||||
/// <param name="result">The parsed command-line result from the <c>smtp update</c> invocation.</param>
|
||||
/// <returns>An <see cref="UpdateSmtpConfigCommand"/> populated from the parsed result.</returns>
|
||||
internal static UpdateSmtpConfigCommand BuildUpdateSmtpConfigCommand(ParseResult result)
|
||||
{
|
||||
var id = result.GetValue(SmtpIdOption);
|
||||
|
||||
@@ -13,6 +13,7 @@ public static class SecurityCommands
|
||||
/// <param name="formatOption">Shared output format option.</param>
|
||||
/// <param name="usernameOption">Shared username option for authentication.</param>
|
||||
/// <param name="passwordOption">Shared password option for authentication.</param>
|
||||
/// <returns>The configured <c>security</c> command with all subcommands attached.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("security") { Description = "Manage security settings" };
|
||||
@@ -125,6 +126,7 @@ public static class SecurityCommands
|
||||
/// The advisory line is written to stderr so that piping stdout captures only the token.
|
||||
/// </summary>
|
||||
/// <param name="json">The JSON success body returned by the management API.</param>
|
||||
/// <returns>Exit code 0.</returns>
|
||||
internal static int PrintCreatedKey(string json)
|
||||
{
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(json);
|
||||
|
||||
@@ -13,6 +13,7 @@ public static class SiteCommands
|
||||
/// <param name="formatOption">Global output format option.</param>
|
||||
/// <param name="usernameOption">Global username option.</param>
|
||||
/// <param name="passwordOption">Global password option.</param>
|
||||
/// <returns>The configured <c>site</c> command with all subcommands attached.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("site") { Description = "Manage sites" };
|
||||
|
||||
@@ -11,6 +11,7 @@ public static class TemplateCommands
|
||||
/// <param name="formatOption">Shared output format option.</param>
|
||||
/// <param name="usernameOption">Shared username option for authentication.</param>
|
||||
/// <param name="passwordOption">Shared password option for authentication.</param>
|
||||
/// <returns>The fully configured <c>template</c> command with all its subcommands.</returns>
|
||||
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
|
||||
{
|
||||
var command = new Command("template") { Description = "Manage templates" };
|
||||
|
||||
@@ -61,6 +61,7 @@ public static class AuditExportEndpoints
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context for the current request.</param>
|
||||
/// <param name="exportService">The export service used to stream audit rows as CSV.</param>
|
||||
/// <returns>A task representing the asynchronous export streaming operation.</returns>
|
||||
internal static async Task HandleExportAsync(HttpContext context, IAuditLogExportService exportService)
|
||||
{
|
||||
var filter = ParseFilter(context.Request.Query);
|
||||
@@ -94,6 +95,7 @@ public static class AuditExportEndpoints
|
||||
/// its own CLI / UI URL builder — so do NOT "fix" the two to one key name.
|
||||
/// </remarks>
|
||||
/// <param name="query">The query string parameters from the HTTP request.</param>
|
||||
/// <returns>An <see cref="AuditLogQueryFilter"/> populated from the query string values.</returns>
|
||||
internal static AuditLogQueryFilter ParseFilter(IQueryCollection query)
|
||||
{
|
||||
var channels = AuditQueryParamParsers.ParseEnumList<AuditChannel>(query["channel"]);
|
||||
|
||||
@@ -19,6 +19,7 @@ public static class AuthEndpoints
|
||||
{
|
||||
/// <summary>Registers the <c>/auth/login</c>, <c>/auth/logout</c>, and <c>/auth/ping</c> endpoints on the given route builder.</summary>
|
||||
/// <param name="endpoints">The route builder to add the endpoints to.</param>
|
||||
/// <returns>The same <paramref name="endpoints"/> instance, for call chaining.</returns>
|
||||
public static IEndpointRouteBuilder MapAuthEndpoints(this IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
endpoints.MapPost("/auth/login", async (HttpContext context) =>
|
||||
@@ -198,6 +199,7 @@ public static class AuthEndpoints
|
||||
/// server-side. See CentralUI-020.
|
||||
/// </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>
|
||||
public static Task HandlePing(HttpContext context)
|
||||
{
|
||||
context.Response.StatusCode = context.User.Identity?.IsAuthenticated == true
|
||||
@@ -219,6 +221,7 @@ public static class AuthEndpoints
|
||||
/// <see cref="AuthenticationProperties.AllowRefresh"/> is left unset (null)
|
||||
/// so the middleware is free to slide the expiry on activity.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="AuthenticationProperties"/> instance with <see cref="AuthenticationProperties.IsPersistent"/> set to <c>true</c> and no fixed expiry.</returns>
|
||||
public static AuthenticationProperties BuildSignInProperties() => new()
|
||||
{
|
||||
IsPersistent = true
|
||||
|
||||
@@ -20,6 +20,7 @@ public static class ClaimsPrincipalExtensions
|
||||
/// <see cref="UnknownUser"/> when the claim is absent.
|
||||
/// </summary>
|
||||
/// <param name="principal">The claims principal to read the username from.</param>
|
||||
/// <returns>The username claim value, or <see cref="UnknownUser"/> if absent.</returns>
|
||||
public static string GetUsername(this ClaimsPrincipal principal)
|
||||
=> principal.FindFirst(JwtTokenService.UsernameClaimType)?.Value ?? UnknownUser;
|
||||
|
||||
@@ -28,6 +29,7 @@ public static class ClaimsPrincipalExtensions
|
||||
/// the claim is absent.
|
||||
/// </summary>
|
||||
/// <param name="principal">The claims principal to read the display name from.</param>
|
||||
/// <returns>The display name claim value, or <c>null</c> if the claim is absent.</returns>
|
||||
public static string? GetDisplayName(this ClaimsPrincipal principal)
|
||||
=> principal.FindFirst(JwtTokenService.DisplayNameClaimType)?.Value;
|
||||
|
||||
@@ -37,6 +39,7 @@ public static class ClaimsPrincipalExtensions
|
||||
/// ten components (CentralUI-024).
|
||||
/// </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>
|
||||
public static async Task<string> GetCurrentUsernameAsync(
|
||||
this AuthenticationStateProvider authStateProvider)
|
||||
{
|
||||
|
||||
@@ -38,6 +38,7 @@ public sealed class SiteScopeService
|
||||
/// True when the user is not restricted to a site subset (no <c>SiteId</c>
|
||||
/// claims). System-wide users see and act on every site.
|
||||
/// </summary>
|
||||
/// <returns>A task that resolves to <c>true</c> if the user has no site-scope restriction.</returns>
|
||||
public async Task<bool> IsSystemWideAsync()
|
||||
=> (await ResolveAsync()).IsSystemWide;
|
||||
|
||||
@@ -46,6 +47,7 @@ public sealed class SiteScopeService
|
||||
/// system-wide user (callers should consult <see cref="IsSystemWideAsync"/>
|
||||
/// or use the filter/allowed helpers, which already account for that).
|
||||
/// </summary>
|
||||
/// <returns>A task that resolves to the set of permitted site IDs (empty for system-wide users).</returns>
|
||||
public async Task<IReadOnlySet<int>> PermittedSiteIdsAsync()
|
||||
=> (await ResolveAsync()).Sites;
|
||||
|
||||
@@ -54,6 +56,7 @@ public sealed class SiteScopeService
|
||||
/// see. A system-wide user gets the full list back unchanged.
|
||||
/// </summary>
|
||||
/// <param name="sites">The full set of sites to filter.</param>
|
||||
/// <returns>A task that resolves to the filtered list of sites the user is permitted to see.</returns>
|
||||
public async Task<List<Site>> FilterSitesAsync(IEnumerable<Site> sites)
|
||||
{
|
||||
var (isSystemWide, allowed) = await ResolveAsync();
|
||||
@@ -67,6 +70,7 @@ public sealed class SiteScopeService
|
||||
/// Must be re-checked server-side before any mutating cross-site command.
|
||||
/// </summary>
|
||||
/// <param name="siteId">The <c>Site.Id</c> to check.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the user may operate on the given site.</returns>
|
||||
public async Task<bool> IsSiteAllowedAsync(int siteId)
|
||||
{
|
||||
var (isSystemWide, allowed) = await ResolveAsync();
|
||||
|
||||
@@ -114,6 +114,7 @@ public sealed class AuditQueryModel
|
||||
/// With one or more Channels selected, the union of the channel-specific kind
|
||||
/// lists is returned (deduplicated and order-stable on first-seen).
|
||||
/// </summary>
|
||||
/// <returns>The deduplicated, order-stable list of <see cref="AuditKind"/> values applicable to the selected channels.</returns>
|
||||
public IReadOnlyList<AuditKind> VisibleKinds()
|
||||
{
|
||||
if (Channels.Count == 0)
|
||||
|
||||
@@ -411,6 +411,7 @@ public partial class AuditResultsGrid : IAsyncDisposable
|
||||
/// </summary>
|
||||
/// <param name="columnKey">The stable key of the resized column.</param>
|
||||
/// <param name="widthPx">The new column width in pixels.</param>
|
||||
/// <returns>A task that completes when the column width has been persisted and the component re-rendered.</returns>
|
||||
[JSInvokable]
|
||||
public async Task OnColumnResized(string columnKey, int widthPx)
|
||||
{
|
||||
@@ -431,6 +432,7 @@ public partial class AuditResultsGrid : IAsyncDisposable
|
||||
/// </summary>
|
||||
/// <param name="fromKey">The stable key of the column being dragged.</param>
|
||||
/// <param name="toKey">The stable key of the target column drop slot.</param>
|
||||
/// <returns>A task that completes when the column order has been persisted and the component re-rendered.</returns>
|
||||
[JSInvokable]
|
||||
public async Task OnColumnReordered(string fromKey, string toKey)
|
||||
{
|
||||
@@ -472,6 +474,7 @@ public partial class AuditResultsGrid : IAsyncDisposable
|
||||
/// <summary>
|
||||
/// Releases the .NET object reference held for JS interop callbacks.
|
||||
/// </summary>
|
||||
/// <returns>A completed value task.</returns>
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
_selfRef?.Dispose();
|
||||
|
||||
@@ -254,6 +254,7 @@ public partial class AuditLogPage : IDisposable
|
||||
/// Builds the CSV export URL for the given filter, encoding all active filter dimensions as query parameters.
|
||||
/// </summary>
|
||||
/// <param name="filter">Currently applied filter; null returns the bare export endpoint.</param>
|
||||
/// <returns>The relative URL with encoded filter dimensions as query parameters.</returns>
|
||||
internal static string BuildExportUrl(AuditLogQueryFilter? filter)
|
||||
{
|
||||
const string basePath = "/api/centralui/audit/export";
|
||||
|
||||
@@ -182,6 +182,7 @@ public partial class TransportExport : ComponentBase
|
||||
/// importer enforces its own strength + lockout policies.
|
||||
/// </summary>
|
||||
/// <param name="s">The passphrase string to score.</param>
|
||||
/// <returns>An integer from 0 (blank) to 4 (long, mixed case, digits, and symbols).</returns>
|
||||
internal static int PassphraseStrength(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s)) return 0;
|
||||
@@ -261,6 +262,7 @@ public partial class TransportExport : ComponentBase
|
||||
/// knows exactly what an unencrypted export would leak.
|
||||
/// </summary>
|
||||
/// <param name="resolved">The resolved export closure whose secret fields are counted.</param>
|
||||
/// <returns>The total number of non-empty secret fields across all external systems, SMTP configs, and database connections.</returns>
|
||||
internal static int CountSecrets(ResolvedExport resolved)
|
||||
{
|
||||
var count = 0;
|
||||
@@ -367,6 +369,7 @@ public partial class TransportExport : ComponentBase
|
||||
/// </summary>
|
||||
/// <param name="sourceEnvironment">The environment label to embed in the filename (sanitised to filename-safe characters).</param>
|
||||
/// <param name="nowUtc">Timestamp to use for the datetime segment; defaults to <see cref="DateTimeOffset.UtcNow"/> when null.</param>
|
||||
/// <returns>A filename of the form <c>scadabundle-{env}-{yyyy-MM-dd-HHmmss}.scadabundle</c>.</returns>
|
||||
internal static string BuildFilename(string sourceEnvironment, DateTimeOffset? nowUtc = null)
|
||||
{
|
||||
var safe = SanitizeForFilename(sourceEnvironment);
|
||||
@@ -427,6 +430,7 @@ public partial class TransportExport : ComponentBase
|
||||
/// <param name="all">The full resolved list including both seed and auto-included items.</param>
|
||||
/// <param name="seed">The set of explicitly selected item ids.</param>
|
||||
/// <param name="idOf">Function that extracts the integer id from an item.</param>
|
||||
/// <returns>Items from <paramref name="all"/> whose ids are not in <paramref name="seed"/> (auto-included dependencies).</returns>
|
||||
internal static IReadOnlyList<T> AutoIncluded<T>(IReadOnlyList<T> all, IReadOnlyCollection<int> seed, Func<T, int> idOf)
|
||||
{
|
||||
return all.Where(x => !seed.Contains(idOf(x))).ToList();
|
||||
|
||||
@@ -18,6 +18,7 @@ internal static class DurationInput
|
||||
/// <c>sec</c> unit.
|
||||
/// </summary>
|
||||
/// <param name="duration">The duration to split, or null for unset.</param>
|
||||
/// <returns>A tuple of the numeric string and unit token (ms/sec/min), or <c>(null, "sec")</c> for null or non-positive input.</returns>
|
||||
internal static (string? Value, string Unit) Split(TimeSpan? duration)
|
||||
{
|
||||
if (duration is not { } d || d <= TimeSpan.Zero) return (null, "sec");
|
||||
@@ -34,6 +35,7 @@ internal static class DurationInput
|
||||
/// </summary>
|
||||
/// <param name="value">The numeric string entered by the user.</param>
|
||||
/// <param name="unit">The selected unit token (ms, sec, or min).</param>
|
||||
/// <returns>The composed <see cref="TimeSpan"/>, or <c>null</c> for blank, unparseable, or non-positive input.</returns>
|
||||
internal static TimeSpan? Compose(string? value, string unit)
|
||||
{
|
||||
if (!long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var n)
|
||||
|
||||
@@ -18,6 +18,7 @@ public interface IDialogService
|
||||
/// <param name="danger">When <c>true</c>, the confirm button renders in
|
||||
/// <c>btn-danger</c> styling with the label "Delete"; otherwise a primary
|
||||
/// "Confirm" button is shown.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> when the user confirms, or <c>false</c> when cancelled.</returns>
|
||||
Task<bool> ConfirmAsync(string title, string message, bool danger = false);
|
||||
|
||||
/// <summary>
|
||||
@@ -28,5 +29,6 @@ public interface IDialogService
|
||||
/// <param name="label">Label rendered above the input field.</param>
|
||||
/// <param name="initialValue">Pre-populated value for the input field.</param>
|
||||
/// <param name="placeholder">Optional placeholder shown when the input is empty.</param>
|
||||
/// <returns>A task that resolves to the entered string, or <c>null</c> if the user cancels.</returns>
|
||||
Task<string?> PromptAsync(string title, string label, string initialValue = "", string? placeholder = null);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ internal static class SchemaBuilderModel
|
||||
/// </summary>
|
||||
/// <param name="json">JSON Schema string to parse, or null/empty to return the fallback.</param>
|
||||
/// <param name="fallback">The <see cref="SchemaNode"/> to return when the input cannot be parsed.</param>
|
||||
/// <returns>The parsed <see cref="SchemaNode"/> tree, or <paramref name="fallback"/> if the input is empty or malformed.</returns>
|
||||
public static SchemaNode Parse(string? json, SchemaNode fallback)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json)) return fallback;
|
||||
@@ -66,15 +67,18 @@ internal static class SchemaBuilderModel
|
||||
}
|
||||
|
||||
/// <summary>Default empty object schema (parameters mode default).</summary>
|
||||
/// <returns>A new <see cref="SchemaNode"/> with type <c>object</c>.</returns>
|
||||
public static SchemaNode NewObject() => new() { Type = "object" };
|
||||
|
||||
/// <summary>Default scalar schema (return mode default).</summary>
|
||||
/// <returns>A new <see cref="SchemaNode"/> with type <c>string</c>.</returns>
|
||||
public static SchemaNode NewValue() => new() { Type = "string" };
|
||||
|
||||
/// <summary>
|
||||
/// Serializes a <see cref="SchemaNode"/> tree to its canonical JSON Schema string.
|
||||
/// </summary>
|
||||
/// <param name="node">The schema node to serialize.</param>
|
||||
/// <returns>The canonical JSON Schema string representing the node tree.</returns>
|
||||
public static string Serialize(SchemaNode node)
|
||||
{
|
||||
using var stream = new System.IO.MemoryStream();
|
||||
|
||||
@@ -13,6 +13,7 @@ public static class ScriptParameterNames
|
||||
/// Parses a parameter definitions JSON Schema and returns the declared parameter names.
|
||||
/// </summary>
|
||||
/// <param name="json">JSON Schema or legacy flat-array string; null/empty returns an empty list.</param>
|
||||
/// <returns>A read-only list of declared parameter names.</returns>
|
||||
public static IReadOnlyList<string> Parse(string? json) =>
|
||||
JsonSchemaShapeParser.ParseParameters(json)
|
||||
.Select(p => p.Name)
|
||||
@@ -23,6 +24,7 @@ public static class ScriptParameterNames
|
||||
/// Parses a parameter definitions JSON Schema and returns the full parameter shape objects.
|
||||
/// </summary>
|
||||
/// <param name="json">JSON Schema or legacy flat-array string; null/empty returns an empty list.</param>
|
||||
/// <returns>A read-only list of parameter shape objects with name and type information.</returns>
|
||||
public static IReadOnlyList<ParameterShape> ParseShapes(string? json) =>
|
||||
JsonSchemaShapeParser.ParseParameters(json);
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ internal static class ScriptTriggerConfigCodec
|
||||
|
||||
/// <summary>Classifies a raw <c>TriggerType</c> string (case-insensitive).</summary>
|
||||
/// <param name="triggerType">The raw trigger type string from the template script entity.</param>
|
||||
/// <returns>The matching <see cref="ScriptTriggerKind"/>, or <see cref="ScriptTriggerKind.None"/> for null/empty.</returns>
|
||||
internal static ScriptTriggerKind ParseKind(string? triggerType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(triggerType)) return ScriptTriggerKind.None;
|
||||
@@ -89,6 +90,7 @@ internal static class ScriptTriggerConfigCodec
|
||||
/// (invoked explicitly, never throttled), and None/Unknown.
|
||||
/// </summary>
|
||||
/// <param name="triggerType">The raw trigger type string to classify.</param>
|
||||
/// <returns><see langword="true"/> if the trigger honours <c>MinTimeBetweenRuns</c>; otherwise <see langword="false"/>.</returns>
|
||||
internal static bool SupportsMinTimeBetweenRuns(string? triggerType) =>
|
||||
ParseKind(triggerType) is ScriptTriggerKind.ValueChange
|
||||
or ScriptTriggerKind.Conditional
|
||||
@@ -96,6 +98,7 @@ internal static class ScriptTriggerConfigCodec
|
||||
|
||||
/// <summary>Canonical <c>TriggerType</c> string for a kind; null for None/Unknown.</summary>
|
||||
/// <param name="kind">The trigger kind to convert.</param>
|
||||
/// <returns>The canonical trigger-type string, or null for <see cref="ScriptTriggerKind.None"/>/<see cref="ScriptTriggerKind.Unknown"/>.</returns>
|
||||
internal static string? KindToString(ScriptTriggerKind kind) => kind switch
|
||||
{
|
||||
ScriptTriggerKind.Interval => "Interval",
|
||||
@@ -113,6 +116,7 @@ internal static class ScriptTriggerConfigCodec
|
||||
/// </summary>
|
||||
/// <param name="json">The raw JSON trigger configuration string.</param>
|
||||
/// <param name="kind">The trigger kind, used to determine which fields to parse.</param>
|
||||
/// <returns>A <see cref="ScriptTriggerModel"/> populated from the JSON; defaults are used for absent or malformed fields.</returns>
|
||||
internal static ScriptTriggerModel Parse(string? json, ScriptTriggerKind kind)
|
||||
{
|
||||
var model = new ScriptTriggerModel();
|
||||
@@ -161,6 +165,7 @@ internal static class ScriptTriggerConfigCodec
|
||||
/// </summary>
|
||||
/// <param name="model">The trigger model to serialize.</param>
|
||||
/// <param name="kind">The trigger kind, used to determine which fields to emit.</param>
|
||||
/// <returns>The JSON configuration string, or null for <see cref="ScriptTriggerKind.None"/>/<see cref="ScriptTriggerKind.Unknown"/>.</returns>
|
||||
internal static string? Serialize(ScriptTriggerModel model, ScriptTriggerKind kind)
|
||||
{
|
||||
if (kind is ScriptTriggerKind.None or ScriptTriggerKind.Unknown) return null;
|
||||
@@ -214,6 +219,7 @@ internal static class ScriptTriggerConfigCodec
|
||||
|
||||
/// <summary>Returns <paramref name="raw"/> if it is a recognized operator, else ">".</summary>
|
||||
/// <param name="raw">The raw operator string to normalize.</param>
|
||||
/// <returns>A valid operator string from <see cref="Operators"/>; defaults to ">" for unrecognized input.</returns>
|
||||
internal static string NormalizeOperator(string? raw)
|
||||
{
|
||||
var op = raw?.Trim();
|
||||
|
||||
@@ -19,6 +19,7 @@ public static class TriggerAttributeMapper
|
||||
{
|
||||
/// <summary>Direct and inherited attributes, exposed as <c>Attributes["..."]</c>.</summary>
|
||||
/// <param name="choices">The full flattened attribute choice list from the trigger editor.</param>
|
||||
/// <returns>The list of <see cref="AttributeShape"/>s for Direct and Inherited attributes.</returns>
|
||||
public static IReadOnlyList<AttributeShape> SelfAttributes(
|
||||
IReadOnlyList<AlarmAttributeChoice> choices) =>
|
||||
choices
|
||||
@@ -32,6 +33,7 @@ public static class TriggerAttributeMapper
|
||||
/// are skipped (no child scope to attach them to).
|
||||
/// </summary>
|
||||
/// <param name="choices">The full flattened attribute choice list from the trigger editor.</param>
|
||||
/// <returns>The list of <see cref="CompositionContext"/>s, one per distinct composition-instance name.</returns>
|
||||
public static IReadOnlyList<CompositionContext> Children(
|
||||
IReadOnlyList<AlarmAttributeChoice> choices) =>
|
||||
choices
|
||||
|
||||
@@ -15,6 +15,7 @@ public static class EndpointExtensions
|
||||
/// </summary>
|
||||
/// <typeparam name="TApp">The root Blazor App component type, supplied by the Host assembly.</typeparam>
|
||||
/// <param name="endpoints">The endpoint route builder to register routes on.</param>
|
||||
/// <returns>The same <paramref name="endpoints"/> instance, for call chaining.</returns>
|
||||
public static IEndpointRouteBuilder MapCentralUI<TApp>(this IEndpointRouteBuilder endpoints)
|
||||
where TApp : Microsoft.AspNetCore.Components.IComponent
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.ScriptAnalysis;
|
||||
public interface ISharedScriptCatalog
|
||||
{
|
||||
/// <summary>Returns the parameter and return shapes for all registered shared scripts.</summary>
|
||||
/// <returns>A task that resolves to the list of all shared script shapes.</returns>
|
||||
Task<IReadOnlyList<ScriptShape>> GetShapesAsync();
|
||||
|
||||
/// <summary>
|
||||
@@ -18,6 +19,7 @@ public interface ISharedScriptCatalog
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the shared script to retrieve.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the async lookup.</param>
|
||||
/// <returns>A task that resolves to the matching shared script source, or null if none exists.</returns>
|
||||
Task<SharedScriptSource?> GetByNameAsync(string name, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public class InboundScriptHost
|
||||
/// Targets a specific instance for method invocation.
|
||||
/// </summary>
|
||||
/// <param name="instanceCode">The instance code to target.</param>
|
||||
/// <returns>A <see cref="RouteTarget"/> scoped to the specified instance.</returns>
|
||||
public RouteTarget To(string instanceCode) => new();
|
||||
}
|
||||
|
||||
@@ -44,6 +45,7 @@ public class InboundScriptHost
|
||||
/// <param name="scriptName">The name of the script to call.</param>
|
||||
/// <param name="parameters">Optional parameters to pass to the script.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the script's return value, or null.</returns>
|
||||
public System.Threading.Tasks.Task<object?> Call(
|
||||
string scriptName,
|
||||
object? parameters = null,
|
||||
@@ -55,6 +57,7 @@ public class InboundScriptHost
|
||||
/// </summary>
|
||||
/// <param name="attributeName">The name of the attribute to retrieve.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the attribute value, or null if not found.</returns>
|
||||
public System.Threading.Tasks.Task<object?> GetAttribute(
|
||||
string attributeName,
|
||||
System.Threading.CancellationToken cancellationToken = default) =>
|
||||
@@ -65,6 +68,7 @@ public class InboundScriptHost
|
||||
/// </summary>
|
||||
/// <param name="attributeNames">The names of the attributes to retrieve.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a dictionary mapping attribute names to their values.</returns>
|
||||
public System.Threading.Tasks.Task<IReadOnlyDictionary<string, object?>> GetAttributes(
|
||||
IEnumerable<string> attributeNames,
|
||||
System.Threading.CancellationToken cancellationToken = default) =>
|
||||
@@ -77,6 +81,7 @@ public class InboundScriptHost
|
||||
/// <param name="attributeName">The name of the attribute to set.</param>
|
||||
/// <param name="value">The value to set.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public System.Threading.Tasks.Task SetAttribute(
|
||||
string attributeName,
|
||||
string value,
|
||||
@@ -88,6 +93,7 @@ public class InboundScriptHost
|
||||
/// </summary>
|
||||
/// <param name="attributeValues">Dictionary of attribute names to values.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public System.Threading.Tasks.Task SetAttributes(
|
||||
IReadOnlyDictionary<string, string> attributeValues,
|
||||
System.Threading.CancellationToken cancellationToken = default) =>
|
||||
|
||||
@@ -20,6 +20,7 @@ public static class JsonSchemaShapeParser
|
||||
{
|
||||
/// <summary>Parses a JSON Schema or legacy flat-array parameters definition and returns the resulting parameter shapes.</summary>
|
||||
/// <param name="json">The JSON string to parse; <c>null</c> or whitespace returns an empty list.</param>
|
||||
/// <returns>A read-only list of <see cref="ParameterShape"/> instances parsed from the definition; empty on null, whitespace, or malformed input.</returns>
|
||||
public static IReadOnlyList<ParameterShape> ParseParameters(string? json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json)) return Array.Empty<ParameterShape>();
|
||||
@@ -41,6 +42,7 @@ public static class JsonSchemaShapeParser
|
||||
|
||||
/// <summary>Parses a JSON Schema or legacy return-type definition and returns the normalised type name, or <c>null</c> if absent or unrecognised.</summary>
|
||||
/// <param name="json">The JSON string to parse; <c>null</c> or whitespace returns <c>null</c>.</param>
|
||||
/// <returns>The normalised type name string (e.g. <c>"Boolean"</c>, <c>"List<Integer>"</c>), or <c>null</c> if the input is null, whitespace, malformed, or unrecognised.</returns>
|
||||
public static string? ParseReturnType(string? json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json)) return null;
|
||||
|
||||
@@ -41,6 +41,7 @@ internal sealed class SandboxConsoleCapture : TextWriter
|
||||
/// <see cref="Console.Error"/> once for the process. Idempotent and
|
||||
/// thread-safe. Subsequent calls return the already-installed instances.
|
||||
/// </summary>
|
||||
/// <returns>The installed <see cref="SandboxConsoleCapture"/> instances for stdout and stderr.</returns>
|
||||
public static (SandboxConsoleCapture Out, SandboxConsoleCapture Error) Install()
|
||||
{
|
||||
if (_outInstance != null && _errorInstance != null)
|
||||
@@ -72,6 +73,7 @@ internal sealed class SandboxConsoleCapture : TextWriter
|
||||
/// other call-trees are unaffected.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The writer that receives console output for this scope.</param>
|
||||
/// <returns>A <see cref="CaptureScope"/> that, when disposed, restores the previous capture state.</returns>
|
||||
public CaptureScope BeginCapture(StringWriter buffer)
|
||||
{
|
||||
var previous = _current.Value;
|
||||
|
||||
@@ -32,6 +32,7 @@ public class SandboxExternalHelper
|
||||
/// <param name="methodName">The method name to invoke.</param>
|
||||
/// <param name="parameters">Optional method parameters.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the <see cref="ExternalCallResult"/> from the external system.</returns>
|
||||
public Task<ExternalCallResult> Call(
|
||||
string systemName,
|
||||
string methodName,
|
||||
@@ -49,6 +50,7 @@ public class SandboxExternalHelper
|
||||
/// <param name="methodName">The method name to invoke.</param>
|
||||
/// <param name="parameters">Optional method parameters.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the <see cref="ExternalCallResult"/> from the external system.</returns>
|
||||
public Task<ExternalCallResult> CachedCall(
|
||||
string systemName,
|
||||
string methodName,
|
||||
@@ -80,6 +82,7 @@ public class SandboxDatabaseHelper
|
||||
/// <summary>Gets a database connection by name.</summary>
|
||||
/// <param name="name">The database connection name.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the open <see cref="DbConnection"/> for the named database.</returns>
|
||||
public Task<DbConnection> Connection(string name, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_gateway == null)
|
||||
@@ -93,6 +96,7 @@ public class SandboxDatabaseHelper
|
||||
/// <param name="sql">The SQL statement to execute.</param>
|
||||
/// <param name="parameters">Optional SQL parameters.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public Task CachedWrite(
|
||||
string name,
|
||||
string sql,
|
||||
@@ -123,6 +127,7 @@ public class SandboxNotifyHelper
|
||||
{
|
||||
/// <summary>Selects the notification list to send to.</summary>
|
||||
/// <param name="listName">The notification list name.</param>
|
||||
/// <returns>A <see cref="SandboxNotifyTarget"/> for the specified list.</returns>
|
||||
public SandboxNotifyTarget To(string listName) =>
|
||||
new();
|
||||
|
||||
@@ -133,6 +138,7 @@ public class SandboxNotifyHelper
|
||||
/// <c>NotifyHelper.Status</c>.
|
||||
/// </summary>
|
||||
/// <param name="notificationId">The notification ID to check status for.</param>
|
||||
/// <returns>A task that resolves to a placeholder <see cref="NotificationDeliveryStatus"/> with status "Unknown".</returns>
|
||||
public Task<NotificationDeliveryStatus> Status(string notificationId) =>
|
||||
Task.FromResult(new NotificationDeliveryStatus("Unknown", 0, null, null));
|
||||
}
|
||||
@@ -156,6 +162,7 @@ public class SandboxNotifyTarget
|
||||
/// <param name="subject">The notification subject.</param>
|
||||
/// <param name="message">The notification message.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a fake notification ID string (a random GUID).</returns>
|
||||
public Task<string> Send(string subject, string message, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult(Guid.NewGuid().ToString("N"));
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public class SandboxInboundScriptHost
|
||||
/// Creates a sandbox route target that throws on every operation.
|
||||
/// </summary>
|
||||
/// <param name="instanceCode">The instance code (used only in the exception message).</param>
|
||||
/// <returns>A sandbox route target bound to <paramref name="instanceCode"/>.</returns>
|
||||
public RouteTarget To(string instanceCode) => new(instanceCode);
|
||||
}
|
||||
|
||||
@@ -50,6 +51,7 @@ public class SandboxInboundScriptHost
|
||||
/// <param name="scriptName">Script name (included in the exception message).</param>
|
||||
/// <param name="parameters">Unused parameters.</param>
|
||||
/// <param name="cancellationToken">Unused token.</param>
|
||||
/// <returns>Never returns; always throws <see cref="ScriptSandboxException"/>.</returns>
|
||||
public Task<object?> Call(
|
||||
string scriptName,
|
||||
object? parameters = null,
|
||||
@@ -61,6 +63,7 @@ public class SandboxInboundScriptHost
|
||||
/// </summary>
|
||||
/// <param name="attributeName">Attribute name (included in the exception message).</param>
|
||||
/// <param name="cancellationToken">Unused token.</param>
|
||||
/// <returns>Never returns; always throws <see cref="ScriptSandboxException"/>.</returns>
|
||||
public Task<object?> GetAttribute(
|
||||
string attributeName,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
@@ -71,6 +74,7 @@ public class SandboxInboundScriptHost
|
||||
/// </summary>
|
||||
/// <param name="attributeNames">Attribute names (unused).</param>
|
||||
/// <param name="cancellationToken">Unused token.</param>
|
||||
/// <returns>Never returns; always throws <see cref="ScriptSandboxException"/>.</returns>
|
||||
public Task<IReadOnlyDictionary<string, object?>> GetAttributes(
|
||||
IEnumerable<string> attributeNames,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
@@ -82,6 +86,7 @@ public class SandboxInboundScriptHost
|
||||
/// <param name="attributeName">Attribute name (included in the exception message).</param>
|
||||
/// <param name="value">Unused value.</param>
|
||||
/// <param name="cancellationToken">Unused token.</param>
|
||||
/// <returns>Never returns; always throws <see cref="ScriptSandboxException"/>.</returns>
|
||||
public Task SetAttribute(
|
||||
string attributeName,
|
||||
string value,
|
||||
@@ -93,6 +98,7 @@ public class SandboxInboundScriptHost
|
||||
/// </summary>
|
||||
/// <param name="attributeValues">Unused attribute values.</param>
|
||||
/// <param name="cancellationToken">Unused token.</param>
|
||||
/// <returns>Never returns; always throws <see cref="ScriptSandboxException"/>.</returns>
|
||||
public Task SetAttributes(
|
||||
IReadOnlyDictionary<string, string> attributeValues,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
|
||||
@@ -102,6 +102,7 @@ public interface ISandboxInstanceGateway
|
||||
/// <param name="canonicalName">The canonical name of the attribute.</param>
|
||||
/// <param name="value">The value to set.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task SetAttributeAsync(string canonicalName, string value, CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,6 +12,7 @@ public static class ScriptShapeParser
|
||||
/// <param name="name">The canonical script name.</param>
|
||||
/// <param name="parametersJson">The JSON Schema or legacy flat-array parameters definition, or <c>null</c> for parameterless scripts.</param>
|
||||
/// <param name="returnJson">The JSON Schema or legacy return-type definition, or <c>null</c> for void scripts.</param>
|
||||
/// <returns>A <see cref="ScriptShape"/> describing the script's parameter list and return type.</returns>
|
||||
public static ScriptShape Parse(string name, string? parametersJson, string? returnJson)
|
||||
{
|
||||
var parameters = JsonSchemaShapeParser.ParseParameters(parametersJson);
|
||||
|
||||
@@ -14,6 +14,7 @@ public static class ServiceCollectionExtensions
|
||||
/// Registers all Central UI services including Blazor, auth state, dialogs, audit query, and script analysis.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to configure.</param>
|
||||
/// <returns>The <paramref name="services"/> instance for chaining.</returns>
|
||||
public static IServiceCollection AddCentralUI(this IServiceCollection services)
|
||||
{
|
||||
services.AddRazorComponents()
|
||||
|
||||
@@ -45,6 +45,7 @@ public static class ApiMethodKeyScopeReconciler
|
||||
/// <param name="currentMethodsByKey">Each affected key's CURRENT full scope set, keyed by KeyId.
|
||||
/// Read fresh from the seam right before reconciling so concurrent edits do not get clobbered.</param>
|
||||
/// <param name="keyNamesById">Display names by KeyId, for human-readable empty-scope messages.</param>
|
||||
/// <returns>A <see cref="ReconcileResult"/> with the scope updates to apply and any empty-scope warnings.</returns>
|
||||
public static ReconcileResult Reconcile(
|
||||
string methodName,
|
||||
IReadOnlySet<string> selectedKeyIds,
|
||||
|
||||
@@ -70,6 +70,8 @@ public sealed record AuditEventView
|
||||
/// <summary>
|
||||
/// Decomposes a canonical <see cref="AuditEvent"/> into a flat view for the UI.
|
||||
/// </summary>
|
||||
/// <param name="evt">The canonical audit event to decompose.</param>
|
||||
/// <returns>A flat <see cref="AuditEventView"/> populated from the event's top-level and details fields.</returns>
|
||||
public static AuditEventView From(AuditEvent evt)
|
||||
{
|
||||
var r = AuditRowProjection.Decompose(evt);
|
||||
|
||||
@@ -42,6 +42,7 @@ public interface IAuditLogExportService
|
||||
/// enough to amortise the per-query overhead, small enough that one page in
|
||||
/// memory is bounded.
|
||||
/// </param>
|
||||
/// <returns>A task that completes when all matching rows (up to <paramref name="maxRows"/>) have been written to <paramref name="output"/>.</returns>
|
||||
Task ExportAsync(
|
||||
AuditLogQueryFilter filter,
|
||||
int maxRows,
|
||||
@@ -176,6 +177,7 @@ public sealed class AuditLogExportService : IAuditLogExportService
|
||||
/// cleanly on another.
|
||||
/// </summary>
|
||||
/// <param name="evt">The audit event view to format as a CSV row.</param>
|
||||
/// <returns>An RFC 4180 CSV row string (no trailing newline) for the supplied event.</returns>
|
||||
internal static string FormatCsvRow(AuditEventView evt)
|
||||
{
|
||||
var sb = new StringBuilder(256);
|
||||
|
||||
@@ -28,6 +28,7 @@ public interface IAuditLogQueryService
|
||||
/// <param name="filter">Filter criteria applied to the audit log query.</param>
|
||||
/// <param name="paging">Optional paging cursor; defaults to first page when null.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a page of audit event views matching the filter.</returns>
|
||||
Task<IReadOnlyList<AuditEventView>> QueryAsync(
|
||||
AuditLogQueryFilter filter,
|
||||
AuditLogPaging? paging = null,
|
||||
@@ -57,6 +58,7 @@ public interface IAuditLogQueryService
|
||||
/// dashboard.
|
||||
/// </remarks>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the current audit KPI snapshot.</returns>
|
||||
Task<AuditLogKpiSnapshot> GetKpiSnapshotAsync(CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -76,6 +78,7 @@ public interface IAuditLogQueryService
|
||||
/// </remarks>
|
||||
/// <param name="executionId">Any execution id in the chain to look up.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the flat list of execution tree nodes in the chain.</returns>
|
||||
Task<IReadOnlyList<ExecutionTreeNode>> GetExecutionTreeAsync(
|
||||
Guid executionId,
|
||||
CancellationToken ct = default);
|
||||
@@ -90,5 +93,6 @@ public interface IAuditLogQueryService
|
||||
/// filter affordance.
|
||||
/// </summary>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the distinct non-null source node names present in the audit log.</returns>
|
||||
Task<IReadOnlyList<string>> GetDistinctSourceNodesAsync(CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public interface IBindingTester
|
||||
/// <param name="connectionName">Name of the site-local data connection — the site's <c>DataConnectionManagerActor</c> indexes its children by name.</param>
|
||||
/// <param name="tagPaths">Tag paths to read.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the read outcomes for all requested tag paths.</returns>
|
||||
Task<ReadTagValuesResult> ReadAsync(
|
||||
string siteId,
|
||||
string connectionName,
|
||||
|
||||
@@ -29,6 +29,7 @@ public interface IBrowseService
|
||||
/// <param name="connectionName">Name of the site-local data connection to browse against — the site's <c>DataConnectionManagerActor</c> indexes its children by name.</param>
|
||||
/// <param name="parentNodeId">Node to browse, or <c>null</c> to browse from the server root.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a <see cref="BrowseNodeResult"/> containing child nodes or a <see cref="BrowseFailure"/> on error.</returns>
|
||||
Task<BrowseNodeResult> BrowseChildrenAsync(
|
||||
string siteId,
|
||||
string connectionName,
|
||||
|
||||
@@ -18,11 +18,7 @@ public sealed class ClusterOptionsValidator : OptionsValidatorBase<ClusterOption
|
||||
"keep-oldest"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Validates the cluster options, recording a failure if any critical settings are misconfigured.
|
||||
/// </summary>
|
||||
/// <param name="builder">The accumulator to record failures on.</param>
|
||||
/// <param name="options">The cluster options to validate.</param>
|
||||
/// <inheritdoc />
|
||||
protected override void Validate(ValidationBuilder builder, ClusterOptions options)
|
||||
{
|
||||
// CI-012: design doc states "both nodes are seed nodes — each node lists
|
||||
|
||||
@@ -23,6 +23,7 @@ public static class ServiceCollectionExtensions
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to register into.</param>
|
||||
/// <returns>The same <paramref name="services"/> instance, for call chaining.</returns>
|
||||
public static IServiceCollection AddClusterInfrastructure(this IServiceCollection services)
|
||||
{
|
||||
services.TryAddEnumerable(
|
||||
|
||||
@@ -20,9 +20,17 @@ public interface IAlarmSubscribableConnection
|
||||
/// currently-active conditions (Snapshot…SnapshotComplete) on every
|
||||
/// (re)subscribe. Returns a subscription id for <see cref="UnsubscribeAlarmsAsync"/>.
|
||||
/// </summary>
|
||||
/// <param name="sourceReference">The source object reference to subscribe alarms for.</param>
|
||||
/// <param name="conditionFilter">Optional condition name filter; <c>null</c> subscribes to all conditions.</param>
|
||||
/// <param name="callback">Delegate invoked on each incoming alarm transition.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the subscription id for use with <see cref="UnsubscribeAlarmsAsync"/>.</returns>
|
||||
Task<string> SubscribeAlarmsAsync(string sourceReference, string? conditionFilter,
|
||||
AlarmTransitionCallback callback, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Cancels an active alarm subscription by its id.</summary>
|
||||
/// <param name="subscriptionId">The subscription id returned by <see cref="SubscribeAlarmsAsync"/>.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UnsubscribeAlarmsAsync(string subscriptionId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ public interface IBrowsableDataConnection
|
||||
/// </summary>
|
||||
/// <param name="parentNodeId">Node id whose children to browse, or null for the server root (OPC UA ObjectsFolder).</param>
|
||||
/// <param name="cancellationToken">Cancellation token; on cancellation the implementation should throw <see cref="OperationCanceledException"/>.</param>
|
||||
/// <returns>A task that resolves to the child nodes and a flag indicating whether results were truncated.</returns>
|
||||
Task<BrowseChildrenResult> BrowseChildrenAsync(
|
||||
string? parentNodeId,
|
||||
CancellationToken cancellationToken = default);
|
||||
@@ -44,5 +45,7 @@ public enum BrowseNodeClass { Object, Variable, Method, Other }
|
||||
/// </summary>
|
||||
public sealed class ConnectionNotConnectedException : InvalidOperationException
|
||||
{
|
||||
/// <summary>Initializes a new <see cref="ConnectionNotConnectedException"/> with the specified error message.</summary>
|
||||
/// <param name="message">Message describing why the connection is not currently connected.</param>
|
||||
public ConnectionNotConnectedException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@ public interface IDataConnection : IAsyncDisposable
|
||||
/// <summary>Establishes the protocol connection using the provided connection details.</summary>
|
||||
/// <param name="connectionDetails">Protocol-specific key-value configuration pairs.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task ConnectAsync(IDictionary<string, string> connectionDetails, CancellationToken cancellationToken = default);
|
||||
/// <summary>Gracefully terminates the protocol connection.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DisconnectAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>Subscribes to value-change notifications for a tag path; returns a subscription ID.</summary>
|
||||
/// <param name="tagPath">The tag path to subscribe to.</param>
|
||||
@@ -31,6 +33,7 @@ public interface IDataConnection : IAsyncDisposable
|
||||
/// <summary>Cancels an active subscription by its ID.</summary>
|
||||
/// <param name="subscriptionId">The subscription ID returned by <see cref="SubscribeAsync"/>.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UnsubscribeAsync(string subscriptionId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Reads the current value of a single tag.</summary>
|
||||
/// <param name="tagPath">The tag path to read.</param>
|
||||
|
||||
@@ -31,6 +31,7 @@ public interface IAuditLogRepository
|
||||
/// </summary>
|
||||
/// <param name="evt">The audit event to insert.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task InsertIfNotExistsAsync(AuditEvent evt, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -44,6 +45,7 @@ public interface IAuditLogRepository
|
||||
/// <param name="filter">Filter criteria to apply to the query.</param>
|
||||
/// <param name="paging">Paging cursor and page size.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching audit events for the requested page, ordered by <c>(OccurredAtUtc DESC, EventId DESC)</c>.</returns>
|
||||
Task<IReadOnlyList<AuditEvent>> QueryAsync(
|
||||
AuditLogQueryFilter filter,
|
||||
AuditLogPaging paging,
|
||||
@@ -82,6 +84,7 @@ public interface IAuditLogRepository
|
||||
/// </remarks>
|
||||
/// <param name="monthBoundary">Lower-bound datetime of the monthly partition to switch out.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the approximate number of rows discarded by the partition switch.</returns>
|
||||
Task<long> SwitchOutPartitionAsync(DateTime monthBoundary, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -94,6 +97,7 @@ public interface IAuditLogRepository
|
||||
/// </summary>
|
||||
/// <param name="threshold">Only partitions whose data is entirely older than this UTC datetime are returned.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the list of partition lower-bound boundaries eligible for purge.</returns>
|
||||
Task<IReadOnlyList<DateTime>> GetPartitionBoundariesOlderThanAsync(
|
||||
DateTime threshold,
|
||||
CancellationToken ct = default);
|
||||
@@ -183,6 +187,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>
|
||||
/// <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,
|
||||
CancellationToken ct = default);
|
||||
@@ -194,5 +199,6 @@ public interface IAuditLogRepository
|
||||
/// for ~60s so the repository is hit at most once per minute per circuit.
|
||||
/// </summary>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the distinct, non-null source node names in ascending order.</returns>
|
||||
Task<IReadOnlyList<string>> GetDistinctSourceNodesAsync(CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -10,30 +10,37 @@ public interface ICentralUiRepository
|
||||
{
|
||||
/// <summary>Returns all configured sites.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of all sites.</returns>
|
||||
Task<IReadOnlyList<Site>> GetAllSitesAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>Returns all data connections for the specified site.</summary>
|
||||
/// <param name="siteId">The site database ID to filter by.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of data connections for the site.</returns>
|
||||
Task<IReadOnlyList<DataConnection>> GetDataConnectionsBySiteIdAsync(int siteId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Returns all data connections across all sites.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of all data connections.</returns>
|
||||
Task<IReadOnlyList<DataConnection>> GetAllDataConnectionsAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>Returns the full template tree including folders and templates.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of templates.</returns>
|
||||
Task<IReadOnlyList<Template>> GetTemplateTreeAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>Returns instances filtered by optional site, template, or search term.</summary>
|
||||
/// <param name="siteId">Optional site ID to filter by.</param>
|
||||
/// <param name="templateId">Optional template ID to filter by.</param>
|
||||
/// <param name="searchTerm">Optional keyword to filter instance names.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of matching instances.</returns>
|
||||
Task<IReadOnlyList<Instance>> GetInstancesFilteredAsync(int? siteId = null, int? templateId = null, string? searchTerm = null, CancellationToken cancellationToken = default);
|
||||
/// <summary>Returns the most recent deployment records up to the specified count.</summary>
|
||||
/// <param name="count">Maximum number of records to return.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of recent deployment records.</returns>
|
||||
Task<IReadOnlyList<DeploymentRecord>> GetRecentDeploymentsAsync(int count, CancellationToken cancellationToken = default);
|
||||
/// <summary>Returns the area tree for the specified site.</summary>
|
||||
/// <param name="siteId">The site database ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of areas for the site.</returns>
|
||||
Task<IReadOnlyList<Area>> GetAreaTreeBySiteIdAsync(int siteId, CancellationToken cancellationToken = default);
|
||||
|
||||
// Audit log queries
|
||||
@@ -51,6 +58,7 @@ public interface ICentralUiRepository
|
||||
/// <param name="page">One-based page number.</param>
|
||||
/// <param name="pageSize">Number of entries per page.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a tuple of the matching entries and the total count.</returns>
|
||||
Task<(IReadOnlyList<AuditLogEntry> Entries, int TotalCount)> GetAuditLogEntriesAsync(
|
||||
string? user = null,
|
||||
string? entityType = null,
|
||||
@@ -66,5 +74,6 @@ public interface ICentralUiRepository
|
||||
|
||||
/// <summary>Persists pending changes to the underlying store.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the number of entities written.</returns>
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public interface IExternalSystemRepository
|
||||
/// </summary>
|
||||
/// <param name="definition">The external system definition to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddExternalSystemAsync(ExternalSystemDefinition definition, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -43,6 +44,7 @@ public interface IExternalSystemRepository
|
||||
/// </summary>
|
||||
/// <param name="definition">The external system definition to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateExternalSystemAsync(ExternalSystemDefinition definition, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -50,6 +52,7 @@ public interface IExternalSystemRepository
|
||||
/// </summary>
|
||||
/// <param name="id">The external system ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteExternalSystemAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// ExternalSystemMethod
|
||||
@@ -86,6 +89,7 @@ public interface IExternalSystemRepository
|
||||
/// </summary>
|
||||
/// <param name="method">The external system method to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddExternalSystemMethodAsync(ExternalSystemMethod method, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -93,6 +97,7 @@ public interface IExternalSystemRepository
|
||||
/// </summary>
|
||||
/// <param name="method">The external system method to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateExternalSystemMethodAsync(ExternalSystemMethod method, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -100,6 +105,7 @@ public interface IExternalSystemRepository
|
||||
/// </summary>
|
||||
/// <param name="id">The method ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteExternalSystemMethodAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// DatabaseConnectionDefinition
|
||||
@@ -135,6 +141,7 @@ public interface IExternalSystemRepository
|
||||
/// </summary>
|
||||
/// <param name="definition">The database connection definition to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddDatabaseConnectionAsync(DatabaseConnectionDefinition definition, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -142,6 +149,7 @@ public interface IExternalSystemRepository
|
||||
/// </summary>
|
||||
/// <param name="definition">The database connection definition to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateDatabaseConnectionAsync(DatabaseConnectionDefinition definition, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -149,6 +157,7 @@ public interface IExternalSystemRepository
|
||||
/// </summary>
|
||||
/// <param name="id">The database connection ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteDatabaseConnectionAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -14,28 +14,35 @@ public interface IInboundApiRepository
|
||||
/// <summary>Retrieves an API method by ID.</summary>
|
||||
/// <param name="id">The API method ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching <see cref="ApiMethod"/>, or <c>null</c> if not found.</returns>
|
||||
Task<ApiMethod?> GetApiMethodByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves all API methods.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of all <see cref="ApiMethod"/> entities.</returns>
|
||||
Task<IReadOnlyList<ApiMethod>> GetAllApiMethodsAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves an API method by name.</summary>
|
||||
/// <param name="name">The API method name.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching <see cref="ApiMethod"/>, or <c>null</c> if not found.</returns>
|
||||
Task<ApiMethod?> GetMethodByNameAsync(string name, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new API method.</summary>
|
||||
/// <param name="method">The API method to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddApiMethodAsync(ApiMethod method, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing API method.</summary>
|
||||
/// <param name="method">The API method to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateApiMethodAsync(ApiMethod method, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes an API method by ID.</summary>
|
||||
/// <param name="id">The API method ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteApiMethodAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Saves pending changes to the database.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the number of state entries written to the database.</returns>
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
+1
@@ -51,6 +51,7 @@ public interface INotificationOutboxRepository
|
||||
/// </summary>
|
||||
/// <param name="n">The notification to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that completes when the notification has been persisted.</returns>
|
||||
Task UpdateAsync(Notification n, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -25,16 +25,19 @@ public interface INotificationRepository
|
||||
/// <summary>Adds a new notification list.</summary>
|
||||
/// <param name="list">The notification list to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
Task AddNotificationListAsync(NotificationList list, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Updates an existing notification list.</summary>
|
||||
/// <param name="list">The notification list to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
Task UpdateNotificationListAsync(NotificationList list, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Deletes a notification list by ID.</summary>
|
||||
/// <param name="id">The notification list ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
Task DeleteNotificationListAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// NotificationRecipient
|
||||
@@ -53,16 +56,19 @@ public interface INotificationRepository
|
||||
/// <summary>Adds a new notification recipient.</summary>
|
||||
/// <param name="recipient">The recipient to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
Task AddRecipientAsync(NotificationRecipient recipient, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Updates an existing notification recipient.</summary>
|
||||
/// <param name="recipient">The recipient to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
Task UpdateRecipientAsync(NotificationRecipient recipient, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Deletes a notification recipient by ID.</summary>
|
||||
/// <param name="id">The recipient ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
Task DeleteRecipientAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// SmtpConfiguration
|
||||
@@ -80,16 +86,19 @@ public interface INotificationRepository
|
||||
/// <summary>Adds a new SMTP configuration.</summary>
|
||||
/// <param name="configuration">The SMTP configuration to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
Task AddSmtpConfigurationAsync(SmtpConfiguration configuration, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Updates an existing SMTP configuration.</summary>
|
||||
/// <param name="configuration">The SMTP configuration to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
Task UpdateSmtpConfigurationAsync(SmtpConfiguration configuration, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Deletes an SMTP configuration by ID.</summary>
|
||||
/// <param name="id">The SMTP configuration ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
Task DeleteSmtpConfigurationAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Saves pending changes to the repository.</summary>
|
||||
|
||||
@@ -39,6 +39,7 @@ public interface ISiteCallAuditRepository
|
||||
/// </summary>
|
||||
/// <param name="siteCall">The site call row to insert or monotonically update.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpsertAsync(SiteCall siteCall, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -46,6 +47,7 @@ public interface ISiteCallAuditRepository
|
||||
/// </summary>
|
||||
/// <param name="id">The tracked operation id to look up.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching <see cref="SiteCall"/>, or <c>null</c> if no row exists.</returns>
|
||||
Task<SiteCall?> GetAsync(TrackedOperationId id, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -58,6 +60,7 @@ public interface ISiteCallAuditRepository
|
||||
/// <param name="filter">Filter criteria for the query.</param>
|
||||
/// <param name="paging">Keyset paging parameters.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a page of <see cref="SiteCall"/> rows matching the filter, ordered by creation time descending.</returns>
|
||||
Task<IReadOnlyList<SiteCall>> QueryAsync(
|
||||
SiteCallQueryFilter filter,
|
||||
SiteCallPaging paging,
|
||||
@@ -71,6 +74,7 @@ public interface ISiteCallAuditRepository
|
||||
/// </summary>
|
||||
/// <param name="olderThanUtc">UTC cutoff; terminal rows older than this are deleted.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the number of rows deleted.</returns>
|
||||
Task<int> PurgeTerminalAsync(DateTime olderThanUtc, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -84,6 +88,7 @@ public interface ISiteCallAuditRepository
|
||||
/// <param name="stuckCutoff">UTC threshold for classifying a row as stuck.</param>
|
||||
/// <param name="intervalSince">UTC start of the delivered/failed interval window.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a global <see cref="SiteCallKpiSnapshot"/> computed from the current table state.</returns>
|
||||
Task<SiteCallKpiSnapshot> ComputeKpisAsync(
|
||||
DateTime stuckCutoff,
|
||||
DateTime intervalSince,
|
||||
@@ -97,6 +102,7 @@ public interface ISiteCallAuditRepository
|
||||
/// <param name="stuckCutoff">UTC threshold for classifying a row as stuck.</param>
|
||||
/// <param name="intervalSince">UTC start of the delivered/failed interval window.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a per-site KPI list; sites with no rows are omitted.</returns>
|
||||
Task<IReadOnlyList<SiteCallSiteKpiSnapshot>> ComputePerSiteKpisAsync(
|
||||
DateTime stuckCutoff,
|
||||
DateTime intervalSince,
|
||||
|
||||
@@ -12,59 +12,73 @@ public interface ISiteRepository
|
||||
/// <summary>Retrieves a site by its ID.</summary>
|
||||
/// <param name="id">The site primary key.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching <see cref="Site"/>, or <c>null</c> if not found.</returns>
|
||||
Task<Site?> GetSiteByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves a site by its identifier.</summary>
|
||||
/// <param name="siteIdentifier">The unique site identifier string.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching <see cref="Site"/>, or <c>null</c> if not found.</returns>
|
||||
Task<Site?> GetSiteByIdentifierAsync(string siteIdentifier, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves all sites.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of all <see cref="Site"/> entities.</returns>
|
||||
Task<IReadOnlyList<Site>> GetAllSitesAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new site.</summary>
|
||||
/// <param name="site">The site entity to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddSiteAsync(Site site, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing site.</summary>
|
||||
/// <param name="site">The site entity with updated values.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateSiteAsync(Site site, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes a site.</summary>
|
||||
/// <param name="id">The site primary key.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteSiteAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// Data Connections
|
||||
/// <summary>Retrieves a data connection by its ID.</summary>
|
||||
/// <param name="id">The data connection primary key.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching <see cref="DataConnection"/>, or <c>null</c> if not found.</returns>
|
||||
Task<DataConnection?> GetDataConnectionByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves all data connections.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of all <see cref="DataConnection"/> entities.</returns>
|
||||
Task<IReadOnlyList<DataConnection>> GetAllDataConnectionsAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves all data connections for a site.</summary>
|
||||
/// <param name="siteId">The site primary key to filter by.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of <see cref="DataConnection"/> entities for the given site.</returns>
|
||||
Task<IReadOnlyList<DataConnection>> GetDataConnectionsBySiteIdAsync(int siteId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new data connection.</summary>
|
||||
/// <param name="connection">The data connection entity to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddDataConnectionAsync(DataConnection connection, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing data connection.</summary>
|
||||
/// <param name="connection">The data connection entity with updated values.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateDataConnectionAsync(DataConnection connection, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes a data connection.</summary>
|
||||
/// <param name="id">The data connection primary key.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteDataConnectionAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// Instances (for deletion constraint checks)
|
||||
/// <summary>Retrieves all instances deployed to a site.</summary>
|
||||
/// <param name="siteId">The site primary key to filter by.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of <see cref="Instance"/> entities for the given site.</returns>
|
||||
Task<IReadOnlyList<Instance>> GetInstancesBySiteIdAsync(int siteId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Saves all pending changes to the database.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the number of state entries written to the database.</returns>
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
+97
@@ -10,10 +10,12 @@ public interface ITemplateEngineRepository
|
||||
/// <summary>Retrieves a template by ID.</summary>
|
||||
/// <param name="id">The template ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching template, or <see langword="null"/> if not found.</returns>
|
||||
Task<Template?> GetTemplateByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves a template with its child entities by ID.</summary>
|
||||
/// <param name="id">The template ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching template with children loaded, or <see langword="null"/> if not found.</returns>
|
||||
Task<Template?> GetTemplateWithChildrenAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>
|
||||
/// Bulk variant of <see cref="GetTemplateWithChildrenAsync(int, CancellationToken)"/>
|
||||
@@ -26,9 +28,11 @@ public interface ITemplateEngineRepository
|
||||
/// </summary>
|
||||
/// <param name="names">Template names to load. Duplicate / null / empty names are filtered out.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of matched templates with children loaded.</returns>
|
||||
Task<IReadOnlyList<Template>> GetTemplatesWithChildrenAsync(IEnumerable<string> names, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves all templates.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of all templates.</returns>
|
||||
Task<IReadOnlyList<Template>> GetAllTemplatesAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>
|
||||
/// Returns every template that contains a composition referencing
|
||||
@@ -38,293 +42,386 @@ public interface ITemplateEngineRepository
|
||||
/// </summary>
|
||||
/// <param name="composedTemplateId">The composed template ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of parent templates that reference the composed template.</returns>
|
||||
Task<IReadOnlyList<Template>> GetTemplatesComposingAsync(int composedTemplateId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new template.</summary>
|
||||
/// <param name="template">The template to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddTemplateAsync(Template template, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing template.</summary>
|
||||
/// <param name="template">The template to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateTemplateAsync(Template template, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes a template by ID.</summary>
|
||||
/// <param name="id">The template ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteTemplateAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// TemplateAttribute
|
||||
/// <summary>Retrieves a template attribute by ID.</summary>
|
||||
/// <param name="id">The attribute ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching attribute, or <see langword="null"/> if not found.</returns>
|
||||
Task<TemplateAttribute?> GetTemplateAttributeByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves attributes for a template.</summary>
|
||||
/// <param name="templateId">The template ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of attributes for the specified template.</returns>
|
||||
Task<IReadOnlyList<TemplateAttribute>> GetAttributesByTemplateIdAsync(int templateId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new template attribute.</summary>
|
||||
/// <param name="attribute">The attribute to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddTemplateAttributeAsync(TemplateAttribute attribute, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing template attribute.</summary>
|
||||
/// <param name="attribute">The attribute to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateTemplateAttributeAsync(TemplateAttribute attribute, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes a template attribute by ID.</summary>
|
||||
/// <param name="id">The attribute ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteTemplateAttributeAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// TemplateAlarm
|
||||
/// <summary>Retrieves a template alarm by ID.</summary>
|
||||
/// <param name="id">The alarm ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching alarm, or <see langword="null"/> if not found.</returns>
|
||||
Task<TemplateAlarm?> GetTemplateAlarmByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves alarms for a template.</summary>
|
||||
/// <param name="templateId">The template ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of alarms for the specified template.</returns>
|
||||
Task<IReadOnlyList<TemplateAlarm>> GetAlarmsByTemplateIdAsync(int templateId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new template alarm.</summary>
|
||||
/// <param name="alarm">The alarm to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddTemplateAlarmAsync(TemplateAlarm alarm, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing template alarm.</summary>
|
||||
/// <param name="alarm">The alarm to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateTemplateAlarmAsync(TemplateAlarm alarm, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes a template alarm by ID.</summary>
|
||||
/// <param name="id">The alarm ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteTemplateAlarmAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// TemplateNativeAlarmSource
|
||||
/// <summary>Retrieves a template native alarm source by ID.</summary>
|
||||
/// <param name="id">The native alarm source ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching native alarm source, or <see langword="null"/> if not found.</returns>
|
||||
Task<TemplateNativeAlarmSource?> GetTemplateNativeAlarmSourceByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves native alarm sources for a template.</summary>
|
||||
/// <param name="templateId">The template ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of native alarm sources for the specified template.</returns>
|
||||
Task<IReadOnlyList<TemplateNativeAlarmSource>> GetNativeAlarmSourcesByTemplateIdAsync(int templateId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new template native alarm source.</summary>
|
||||
/// <param name="source">The native alarm source to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddTemplateNativeAlarmSourceAsync(TemplateNativeAlarmSource source, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing template native alarm source.</summary>
|
||||
/// <param name="source">The native alarm source to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateTemplateNativeAlarmSourceAsync(TemplateNativeAlarmSource source, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes a template native alarm source by ID.</summary>
|
||||
/// <param name="id">The native alarm source ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteTemplateNativeAlarmSourceAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// TemplateScript
|
||||
/// <summary>Retrieves a template script by ID.</summary>
|
||||
/// <param name="id">The script ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching script, or <see langword="null"/> if not found.</returns>
|
||||
Task<TemplateScript?> GetTemplateScriptByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves scripts for a template.</summary>
|
||||
/// <param name="templateId">The template ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of scripts for the specified template.</returns>
|
||||
Task<IReadOnlyList<TemplateScript>> GetScriptsByTemplateIdAsync(int templateId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new template script.</summary>
|
||||
/// <param name="script">The script to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddTemplateScriptAsync(TemplateScript script, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing template script.</summary>
|
||||
/// <param name="script">The script to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateTemplateScriptAsync(TemplateScript script, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes a template script by ID.</summary>
|
||||
/// <param name="id">The script ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteTemplateScriptAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// TemplateComposition
|
||||
/// <summary>Retrieves a template composition by ID.</summary>
|
||||
/// <param name="id">The composition ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching composition, or <see langword="null"/> if not found.</returns>
|
||||
Task<TemplateComposition?> GetTemplateCompositionByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves compositions for a template.</summary>
|
||||
/// <param name="templateId">The template ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of compositions for the specified template.</returns>
|
||||
Task<IReadOnlyList<TemplateComposition>> GetCompositionsByTemplateIdAsync(int templateId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new template composition.</summary>
|
||||
/// <param name="composition">The composition to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddTemplateCompositionAsync(TemplateComposition composition, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing template composition.</summary>
|
||||
/// <param name="composition">The composition to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateTemplateCompositionAsync(TemplateComposition composition, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes a template composition by ID.</summary>
|
||||
/// <param name="id">The composition ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteTemplateCompositionAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// Instance
|
||||
/// <summary>Retrieves an instance by ID.</summary>
|
||||
/// <param name="id">The instance ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching instance, or <see langword="null"/> if not found.</returns>
|
||||
Task<Instance?> GetInstanceByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves all instances.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of all instances.</returns>
|
||||
Task<IReadOnlyList<Instance>> GetAllInstancesAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves instances for a template.</summary>
|
||||
/// <param name="templateId">The template ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of instances for the specified template.</returns>
|
||||
Task<IReadOnlyList<Instance>> GetInstancesByTemplateIdAsync(int templateId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves instances for a site.</summary>
|
||||
/// <param name="siteId">The site ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of instances for the specified site.</returns>
|
||||
Task<IReadOnlyList<Instance>> GetInstancesBySiteIdAsync(int siteId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves an instance by unique name.</summary>
|
||||
/// <param name="uniqueName">The unique instance name.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching instance, or <see langword="null"/> if not found.</returns>
|
||||
Task<Instance?> GetInstanceByUniqueNameAsync(string uniqueName, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new instance.</summary>
|
||||
/// <param name="instance">The instance to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddInstanceAsync(Instance instance, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing instance.</summary>
|
||||
/// <param name="instance">The instance to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateInstanceAsync(Instance instance, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes an instance by ID.</summary>
|
||||
/// <param name="id">The instance ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteInstanceAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// InstanceAttributeOverride
|
||||
/// <summary>Retrieves attribute overrides for an instance.</summary>
|
||||
/// <param name="instanceId">The instance ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of attribute overrides for the specified instance.</returns>
|
||||
Task<IReadOnlyList<InstanceAttributeOverride>> GetOverridesByInstanceIdAsync(int instanceId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new instance attribute override.</summary>
|
||||
/// <param name="attributeOverride">The override to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddInstanceAttributeOverrideAsync(InstanceAttributeOverride attributeOverride, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing instance attribute override.</summary>
|
||||
/// <param name="attributeOverride">The override to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateInstanceAttributeOverrideAsync(InstanceAttributeOverride attributeOverride, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes an instance attribute override by ID.</summary>
|
||||
/// <param name="id">The override ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteInstanceAttributeOverrideAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// InstanceAlarmOverride
|
||||
/// <summary>Retrieves alarm overrides for an instance.</summary>
|
||||
/// <param name="instanceId">The instance ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of alarm overrides for the specified instance.</returns>
|
||||
Task<IReadOnlyList<InstanceAlarmOverride>> GetAlarmOverridesByInstanceIdAsync(int instanceId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves an alarm override by instance and alarm name.</summary>
|
||||
/// <param name="instanceId">The instance ID.</param>
|
||||
/// <param name="alarmCanonicalName">The alarm canonical name.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching alarm override, or <see langword="null"/> if not found.</returns>
|
||||
Task<InstanceAlarmOverride?> GetAlarmOverrideAsync(int instanceId, string alarmCanonicalName, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new instance alarm override.</summary>
|
||||
/// <param name="alarmOverride">The override to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddInstanceAlarmOverrideAsync(InstanceAlarmOverride alarmOverride, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing instance alarm override.</summary>
|
||||
/// <param name="alarmOverride">The override to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateInstanceAlarmOverrideAsync(InstanceAlarmOverride alarmOverride, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes an instance alarm override by ID.</summary>
|
||||
/// <param name="id">The override ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteInstanceAlarmOverrideAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// InstanceNativeAlarmSourceOverride
|
||||
/// <summary>Retrieves native alarm source overrides for an instance.</summary>
|
||||
/// <param name="instanceId">The instance ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of native alarm source overrides for the specified instance.</returns>
|
||||
Task<IReadOnlyList<InstanceNativeAlarmSourceOverride>> GetNativeAlarmSourceOverridesByInstanceIdAsync(int instanceId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves a single native alarm source override by instance + source canonical name.</summary>
|
||||
/// <param name="instanceId">The instance ID.</param>
|
||||
/// <param name="sourceCanonicalName">The canonical name of the native alarm source.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching override, or <see langword="null"/> if not found.</returns>
|
||||
Task<InstanceNativeAlarmSourceOverride?> GetNativeAlarmSourceOverrideAsync(int instanceId, string sourceCanonicalName, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new instance native alarm source override.</summary>
|
||||
/// <param name="ovr">The override to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddInstanceNativeAlarmSourceOverrideAsync(InstanceNativeAlarmSourceOverride ovr, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing instance native alarm source override.</summary>
|
||||
/// <param name="ovr">The override to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateInstanceNativeAlarmSourceOverrideAsync(InstanceNativeAlarmSourceOverride ovr, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes an instance native alarm source override by ID.</summary>
|
||||
/// <param name="id">The override ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteInstanceNativeAlarmSourceOverrideAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// InstanceConnectionBinding
|
||||
/// <summary>Retrieves connection bindings for an instance.</summary>
|
||||
/// <param name="instanceId">The instance ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of connection bindings for the specified instance.</returns>
|
||||
Task<IReadOnlyList<InstanceConnectionBinding>> GetBindingsByInstanceIdAsync(int instanceId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new instance connection binding.</summary>
|
||||
/// <param name="binding">The binding to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddInstanceConnectionBindingAsync(InstanceConnectionBinding binding, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing instance connection binding.</summary>
|
||||
/// <param name="binding">The binding to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateInstanceConnectionBindingAsync(InstanceConnectionBinding binding, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes an instance connection binding by ID.</summary>
|
||||
/// <param name="id">The binding ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteInstanceConnectionBindingAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// Area
|
||||
/// <summary>Retrieves an area by ID.</summary>
|
||||
/// <param name="id">The area ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching area, or <see langword="null"/> if not found.</returns>
|
||||
Task<Area?> GetAreaByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves areas for a site.</summary>
|
||||
/// <param name="siteId">The site ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of areas for the specified site.</returns>
|
||||
Task<IReadOnlyList<Area>> GetAreasBySiteIdAsync(int siteId, CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new area.</summary>
|
||||
/// <param name="area">The area to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddAreaAsync(Area area, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing area.</summary>
|
||||
/// <param name="area">The area to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateAreaAsync(Area area, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes an area by ID.</summary>
|
||||
/// <param name="id">The area ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteAreaAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// SharedScript
|
||||
/// <summary>Retrieves a shared script by ID.</summary>
|
||||
/// <param name="id">The script ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching shared script, or <see langword="null"/> if not found.</returns>
|
||||
Task<SharedScript?> GetSharedScriptByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves a shared script by name.</summary>
|
||||
/// <param name="name">The script name.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching shared script, or <see langword="null"/> if not found.</returns>
|
||||
Task<SharedScript?> GetSharedScriptByNameAsync(string name, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves all shared scripts.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of all shared scripts.</returns>
|
||||
Task<IReadOnlyList<SharedScript>> GetAllSharedScriptsAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new shared script.</summary>
|
||||
/// <param name="sharedScript">The script to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddSharedScriptAsync(SharedScript sharedScript, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing shared script.</summary>
|
||||
/// <param name="sharedScript">The script to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateSharedScriptAsync(SharedScript sharedScript, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes a shared script by ID.</summary>
|
||||
/// <param name="id">The script ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteSharedScriptAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
// TemplateFolder
|
||||
/// <summary>Retrieves a template folder by ID.</summary>
|
||||
/// <param name="id">The folder ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching folder, or <see langword="null"/> if not found.</returns>
|
||||
Task<TemplateFolder?> GetFolderByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
/// <summary>Retrieves all template folders.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a read-only list of all template folders.</returns>
|
||||
Task<IReadOnlyList<TemplateFolder>> GetAllFoldersAsync(CancellationToken cancellationToken = default);
|
||||
/// <summary>Adds a new template folder.</summary>
|
||||
/// <param name="folder">The folder to add.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task AddFolderAsync(TemplateFolder folder, CancellationToken cancellationToken = default);
|
||||
/// <summary>Updates an existing template folder.</summary>
|
||||
/// <param name="folder">The folder to update.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateFolderAsync(TemplateFolder folder, CancellationToken cancellationToken = default);
|
||||
/// <summary>Deletes a template folder by ID.</summary>
|
||||
/// <param name="id">The folder ID.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task DeleteFolderAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Saves pending changes to the database.</summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the number of rows written to the database.</returns>
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -45,31 +45,54 @@ public interface IInboundApiKeyAdmin
|
||||
{
|
||||
/// <summary>Creates a new key scoped to <paramref name="methods"/> and returns its
|
||||
/// identifier plus the bearer token (shown once).</summary>
|
||||
/// <param name="name">Operator-facing display name for the new key.</param>
|
||||
/// <param name="methods">API method names the key is permitted to call.</param>
|
||||
/// <param name="ct">Token to observe for cancellation.</param>
|
||||
/// <returns>A task that resolves to the new key identifier and the one-time bearer token.</returns>
|
||||
Task<InboundApiKeyCreated> CreateAsync(
|
||||
string name, IReadOnlyCollection<string> methods, CancellationToken ct = default);
|
||||
|
||||
/// <summary>Lists all inbound keys (hash-free projection).</summary>
|
||||
/// <param name="ct">Token to observe for cancellation.</param>
|
||||
/// <returns>A task that resolves to the full list of inbound API key records.</returns>
|
||||
Task<IReadOnlyList<InboundApiKeyInfo>> ListAsync(CancellationToken ct = default);
|
||||
|
||||
/// <summary>Enables or disables a key without changing its secret. Returns false if
|
||||
/// the key does not exist.</summary>
|
||||
/// <param name="keyId">Identifier of the key to update.</param>
|
||||
/// <param name="enabled">True to enable the key; false to disable it.</param>
|
||||
/// <param name="ct">Token to observe for cancellation.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the key was updated; <c>false</c> if the key does not exist.</returns>
|
||||
Task<bool> SetEnabledAsync(string keyId, bool enabled, CancellationToken ct = default);
|
||||
|
||||
/// <summary>Replaces the method-scope set on a key without changing its secret.
|
||||
/// Returns false if the key does not exist.</summary>
|
||||
/// <param name="keyId">Identifier of the key to update.</param>
|
||||
/// <param name="methods">Replacement set of API method names the key may call.</param>
|
||||
/// <param name="ct">Token to observe for cancellation.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the key's method scope was replaced; <c>false</c> if the key does not exist.</returns>
|
||||
Task<bool> SetMethodsAsync(
|
||||
string keyId, IReadOnlyCollection<string> methods, CancellationToken ct = default);
|
||||
|
||||
/// <summary>Removes a key (revoke-then-delete). Returns false if the key could not be
|
||||
/// deleted.</summary>
|
||||
/// <param name="keyId">Identifier of the key to delete.</param>
|
||||
/// <param name="ct">Token to observe for cancellation.</param>
|
||||
/// <returns>A task that resolves to <c>true</c> if the key was deleted; <c>false</c> if it could not be deleted.</returns>
|
||||
Task<bool> DeleteAsync(string keyId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>Returns the method-scope set for a key, or an empty list if not found.</summary>
|
||||
/// <remarks>Enumerates the full key list (O(n)); intended for admin-scale use, not hot paths.</remarks>
|
||||
/// <param name="keyId">Identifier of the key whose method scope to retrieve.</param>
|
||||
/// <param name="ct">Token to observe for cancellation.</param>
|
||||
/// <returns>A task that resolves to the method names the key is scoped to, or an empty list if the key does not exist.</returns>
|
||||
Task<IReadOnlyList<string>> GetMethodsForKeyAsync(string keyId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>Returns the identifiers of all keys whose scopes contain
|
||||
/// <paramref name="methodName"/>.</summary>
|
||||
/// <remarks>Enumerates the full key list (O(n)); intended for admin-scale use, not hot paths.</remarks>
|
||||
/// <param name="methodName">API method name to search for across all key scopes.</param>
|
||||
/// <param name="ct">Token to observe for cancellation.</param>
|
||||
/// <returns>A task that resolves to the identifiers of all keys whose scopes include <paramref name="methodName"/>.</returns>
|
||||
Task<IReadOnlyList<string>> GetKeysForMethodAsync(string methodName, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -12,5 +12,6 @@ public interface IAuditService
|
||||
/// <param name="entityName">The display name of the affected entity.</param>
|
||||
/// <param name="afterState">The entity state after the action; may be null for deletes.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the log write.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task LogAsync(string user, string action, string entityType, string entityId, string entityName, object? afterState, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -21,5 +21,6 @@ public interface IAuditWriter
|
||||
/// </summary>
|
||||
/// <param name="evt">The audit event to persist.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task representing the asynchronous write operation.</returns>
|
||||
Task WriteAsync(AuditEvent evt, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public interface ICachedCallLifecycleObserver
|
||||
/// </summary>
|
||||
/// <param name="context">Per-attempt context including the tracking id, outcome, and audit provenance fields.</param>
|
||||
/// <param name="ct">Cancellation token for the observation operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task OnAttemptCompletedAsync(CachedCallAttemptContext context, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,5 +32,6 @@ public interface ICachedCallTelemetryForwarder
|
||||
/// </summary>
|
||||
/// <param name="telemetry">The combined-telemetry packet to fan out.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task ForwardAsync(CachedCallTelemetry telemetry, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@ public interface ICentralAuditWriter
|
||||
/// </summary>
|
||||
/// <param name="evt">The audit event to persist.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task WriteAsync(AuditEvent evt, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ public interface IDatabaseGateway
|
||||
/// </summary>
|
||||
/// <param name="connectionName">Name of the configured database connection to open.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the async open operation.</param>
|
||||
/// <returns>A task that resolves to an open <see cref="DbConnection"/>; the caller is responsible for disposing it.</returns>
|
||||
Task<DbConnection> GetConnectionAsync(
|
||||
string connectionName,
|
||||
CancellationToken cancellationToken = default);
|
||||
@@ -55,6 +56,7 @@ public interface IDatabaseGateway
|
||||
/// <param name="parameters">Optional SQL parameters for the statement.</param>
|
||||
/// <param name="originInstanceName">Optional name of the instance that originated the write.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the buffering operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task CachedWriteAsync(
|
||||
string connectionName,
|
||||
string sql,
|
||||
|
||||
@@ -12,6 +12,7 @@ public interface IInstanceLocator
|
||||
/// </summary>
|
||||
/// <param name="instanceUniqueName">System-wide unique name of the instance to look up.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the site identifier for the instance, or <c>null</c> if the instance is not found.</returns>
|
||||
Task<string?> GetSiteIdForInstanceAsync(
|
||||
string instanceUniqueName,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
@@ -47,6 +47,7 @@ public interface IOperationTrackingStore
|
||||
/// <param name="sourceScript">Optional name of the source script.</param>
|
||||
/// <param name="sourceNode">Optional source node identifier.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task RecordEnqueueAsync(
|
||||
TrackedOperationId id,
|
||||
string kind,
|
||||
@@ -68,6 +69,7 @@ public interface IOperationTrackingStore
|
||||
/// <param name="lastError">Optional error message from the last attempt.</param>
|
||||
/// <param name="httpStatus">Optional HTTP status code from the last attempt.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task RecordAttemptAsync(
|
||||
TrackedOperationId id,
|
||||
string status,
|
||||
@@ -86,6 +88,7 @@ public interface IOperationTrackingStore
|
||||
/// <param name="lastError">Optional final error message.</param>
|
||||
/// <param name="httpStatus">Optional final HTTP status code.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task RecordTerminalAsync(
|
||||
TrackedOperationId id,
|
||||
string status,
|
||||
@@ -111,6 +114,7 @@ public interface IOperationTrackingStore
|
||||
/// </summary>
|
||||
/// <param name="olderThanUtc">Cutoff timestamp; rows terminal before this are deleted.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task PurgeTerminalAsync(
|
||||
DateTime olderThanUtc,
|
||||
CancellationToken ct = default);
|
||||
|
||||
@@ -44,6 +44,7 @@ public interface IPartitionMaintenance
|
||||
/// </summary>
|
||||
/// <param name="lookaheadMonths">Number of future monthly boundaries to ensure exist.</param>
|
||||
/// <param name="ct">Cancellation token for the SQL operation.</param>
|
||||
/// <returns>A task that resolves to the list of boundary values actually added, in chronological order.</returns>
|
||||
Task<IReadOnlyList<DateTime>> EnsureLookaheadAsync(int lookaheadMonths, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -53,5 +54,6 @@ public interface IPartitionMaintenance
|
||||
/// has no boundaries.
|
||||
/// </summary>
|
||||
/// <param name="ct">Cancellation token for the SQL operation.</param>
|
||||
/// <returns>A task that resolves to the highest boundary value, or <c>null</c> when the partition function has no boundaries.</returns>
|
||||
Task<DateTime?> GetMaxBoundaryAsync(CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ public interface ISiteAuditQueue
|
||||
/// </remarks>
|
||||
/// <param name="limit">Maximum number of rows to return.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the oldest pending non-cached audit events, up to <paramref name="limit"/>.</returns>
|
||||
Task<IReadOnlyList<AuditEvent>> ReadPendingAsync(int limit, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -77,6 +78,7 @@ public interface ISiteAuditQueue
|
||||
/// </remarks>
|
||||
/// <param name="limit">Maximum number of rows to return.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the oldest pending cached-lifecycle audit events, up to <paramref name="limit"/>.</returns>
|
||||
Task<IReadOnlyList<AuditEvent>> ReadPendingCachedTelemetryAsync(int limit, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -87,6 +89,7 @@ public interface ISiteAuditQueue
|
||||
/// </summary>
|
||||
/// <param name="eventIds">Event IDs to mark as forwarded.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task MarkForwardedAsync(IReadOnlyList<Guid> eventIds, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -107,6 +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="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to audit events at or after <paramref name="sinceUtc"/> in pending or forwarded state, up to <paramref name="batchSize"/>.</returns>
|
||||
Task<IReadOnlyList<AuditEvent>> ReadPendingSinceAsync(
|
||||
DateTime sinceUtc, int batchSize, CancellationToken ct = default);
|
||||
|
||||
@@ -121,6 +125,7 @@ public interface ISiteAuditQueue
|
||||
/// </summary>
|
||||
/// <param name="eventIds">Event IDs to mark as reconciled.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task MarkReconciledAsync(IReadOnlyList<Guid> eventIds, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -135,5 +140,6 @@ public interface ISiteAuditQueue
|
||||
/// the hot-path INSERT batch and the drain queries.
|
||||
/// </summary>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a point-in-time snapshot of the site audit queue's pending count, oldest timestamp, and on-disk file size.</returns>
|
||||
Task<SiteAuditBacklogSnapshot> GetBacklogStatsAsync(CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ public interface IBundleExporter
|
||||
/// <param name="sourceEnvironment">Environment label stamped in the bundle manifest.</param>
|
||||
/// <param name="passphrase">Optional passphrase to encrypt the bundle; null produces an unencrypted bundle.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a seeked-to-start stream containing the bundle ZIP archive.</returns>
|
||||
Task<Stream> ExportAsync(
|
||||
ExportSelection selection,
|
||||
string user,
|
||||
|
||||
@@ -10,6 +10,7 @@ public interface IBundleImporter
|
||||
/// <param name="bundleStream">Stream containing the bundle zip archive.</param>
|
||||
/// <param name="passphrase">Optional passphrase for decrypting an encrypted bundle.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to session metadata for the loaded bundle.</returns>
|
||||
Task<BundleSession> LoadAsync(Stream bundleStream, string? passphrase, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -17,6 +18,7 @@ public interface IBundleImporter
|
||||
/// </summary>
|
||||
/// <param name="sessionId">Session id returned by <see cref="LoadAsync"/>.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a per-artifact import preview with conflict details.</returns>
|
||||
Task<ImportPreview> PreviewAsync(Guid sessionId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
@@ -26,6 +28,7 @@ public interface IBundleImporter
|
||||
/// <param name="resolutions">Per-artifact conflict resolutions from the preview step.</param>
|
||||
/// <param name="user">Username of the operator performing the import, stamped in audit rows.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the result of the committed import transaction.</returns>
|
||||
Task<ImportResult> ApplyAsync(
|
||||
Guid sessionId,
|
||||
IReadOnlyList<ImportResolution> resolutions,
|
||||
|
||||
@@ -6,9 +6,11 @@ public interface IBundleSessionStore
|
||||
{
|
||||
/// <summary>Stores the session and returns it; overwrites any existing session with the same id.</summary>
|
||||
/// <param name="session">The session to store.</param>
|
||||
/// <returns>The stored session (same reference as <paramref name="session"/>).</returns>
|
||||
BundleSession Open(BundleSession session);
|
||||
/// <summary>Returns the session for the given id, or null if not found or expired.</summary>
|
||||
/// <param name="sessionId">The session identifier to look up.</param>
|
||||
/// <returns>The matching <see cref="BundleSession"/>, or null if not found or expired.</returns>
|
||||
BundleSession? Get(Guid sessionId);
|
||||
/// <summary>Removes the session for the given id, if present.</summary>
|
||||
/// <param name="sessionId">The session identifier to remove.</param>
|
||||
@@ -31,6 +33,7 @@ public interface IBundleSessionStore
|
||||
/// against identical bundle bytes are throttled regardless of client.
|
||||
/// </summary>
|
||||
/// <param name="bundleContentHash">SHA-256 hex from <c>BundleManifest.ContentHash</c>.</param>
|
||||
/// <returns>The new unlock-failure count after incrementing.</returns>
|
||||
int IncrementUnlockFailureCount(string bundleContentHash);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -29,6 +29,7 @@ public static class ManagementCommandRegistry
|
||||
/// Resolves a management command wire name to its CLR type, or null if not registered.
|
||||
/// </summary>
|
||||
/// <param name="commandName">The wire name of the management command (without the "Command" suffix).</param>
|
||||
/// <returns>The CLR <see cref="Type"/> for the command, or <c>null</c> if not registered.</returns>
|
||||
public static Type? Resolve(string commandName)
|
||||
{
|
||||
return Commands.GetValueOrDefault(commandName);
|
||||
@@ -45,6 +46,7 @@ public static class ManagementCommandRegistry
|
||||
/// symmetric with <see cref="Resolve"/>: it never yields a name that
|
||||
/// <see cref="Resolve"/> cannot turn back into the same type.
|
||||
/// </exception>
|
||||
/// <returns>The registered wire name for <paramref name="commandType"/>.</returns>
|
||||
public static string GetCommandName(Type commandType)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(commandType);
|
||||
|
||||
@@ -21,11 +21,13 @@ public static class MxGatewayEndpointConfigSerializer
|
||||
|
||||
/// <summary>Serializes a config to the typed JSON shape.</summary>
|
||||
/// <param name="config">The endpoint configuration to serialize.</param>
|
||||
/// <returns>A camelCase JSON string representing the endpoint configuration.</returns>
|
||||
public static string Serialize(MxGatewayEndpointConfig config)
|
||||
=> JsonSerializer.Serialize(config, JsonOpts);
|
||||
|
||||
/// <summary>Parses stored config JSON; null/blank/malformed yields a default config.</summary>
|
||||
/// <param name="json">The stored JSON string.</param>
|
||||
/// <returns>The deserialized <see cref="MxGatewayEndpointConfig"/>, or a default instance if the input is null, blank, or malformed.</returns>
|
||||
public static MxGatewayEndpointConfig Deserialize(string? json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json)) return new MxGatewayEndpointConfig();
|
||||
@@ -35,6 +37,7 @@ public static class MxGatewayEndpointConfigSerializer
|
||||
|
||||
/// <summary>Flattens the typed config to the key-value shape the adapter consumes.</summary>
|
||||
/// <param name="c">The endpoint configuration to flatten.</param>
|
||||
/// <returns>A string-keyed dictionary containing all endpoint configuration properties.</returns>
|
||||
public static IDictionary<string, string> ToFlatDict(MxGatewayEndpointConfig c) => new Dictionary<string, string>
|
||||
{
|
||||
["Endpoint"] = c.Endpoint,
|
||||
@@ -49,6 +52,7 @@ public static class MxGatewayEndpointConfigSerializer
|
||||
|
||||
/// <summary>Reconstructs a config from the flat key-value shape; invalid numerics fall back to defaults.</summary>
|
||||
/// <param name="d">The flat dictionary.</param>
|
||||
/// <returns>A <see cref="MxGatewayEndpointConfig"/> populated from the dictionary; missing or invalid entries use default values.</returns>
|
||||
public static MxGatewayEndpointConfig FromFlatDict(IDictionary<string, string> d)
|
||||
{
|
||||
var c = new MxGatewayEndpointConfig();
|
||||
|
||||
@@ -118,6 +118,7 @@ public static class OpcUaEndpointConfigSerializer
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <param name="json">The stored JSON string to parse; null or blank yields a default typed result.</param>
|
||||
/// <returns>An <see cref="OpcUaConfigParseResult"/> containing the parsed config and the detected parse status.</returns>
|
||||
public static OpcUaConfigParseResult Deserialize(string? json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
@@ -175,6 +176,7 @@ public static class OpcUaEndpointConfigSerializer
|
||||
/// used by OpcUaDataConnection so the adapter can keep that interface.
|
||||
/// </summary>
|
||||
/// <param name="config">The endpoint configuration to flatten.</param>
|
||||
/// <returns>A dictionary mapping connection-parameter key names to their string values.</returns>
|
||||
public static IDictionary<string, string> ToFlatDict(OpcUaEndpointConfig config)
|
||||
{
|
||||
var dict = new Dictionary<string, string>
|
||||
|
||||
@@ -10,6 +10,9 @@ public static class AlarmConditionStateFactory
|
||||
/// auto-acked, never shelved or suppressed, not confirmable, and their
|
||||
/// severity is the configured priority. Active mirrors the alarm State.
|
||||
/// </summary>
|
||||
/// <param name="state">The current alarm state used to derive the Active flag.</param>
|
||||
/// <param name="priority">Configured priority mapped to the Severity field (0–1000).</param>
|
||||
/// <returns>An <see cref="AlarmConditionState"/> reflecting the computed alarm's lifecycle (auto-acked, unshelved, not suppressed).</returns>
|
||||
public static AlarmConditionState ForComputed(AlarmState state, int priority) =>
|
||||
new(Active: state == AlarmState.Active, Acknowledged: true, Confirmed: null,
|
||||
Shelve: AlarmShelveState.Unshelved, Suppressed: false, Severity: priority);
|
||||
|
||||
@@ -46,6 +46,8 @@ public static class AuditDetailsCodec
|
||||
/// Serializes <paramref name="details"/> to a compact, deterministic JSON string
|
||||
/// suitable for storage in <c>AuditEvent.DetailsJson</c>.
|
||||
/// </summary>
|
||||
/// <param name="details">The audit details instance to serialize.</param>
|
||||
/// <returns>A compact, deterministic JSON string representing the audit details.</returns>
|
||||
public static string Serialize(AuditDetails details)
|
||||
=> JsonSerializer.Serialize(details, Options);
|
||||
|
||||
@@ -54,6 +56,8 @@ public static class AuditDetailsCodec
|
||||
/// Returns an empty (all-null) <see cref="AuditDetails"/> when <paramref name="json"/>
|
||||
/// is <c>null</c>, empty, or whitespace — never throws.
|
||||
/// </summary>
|
||||
/// <param name="json">The JSON string to deserialize; null or whitespace returns an empty instance.</param>
|
||||
/// <returns>The deserialized <see cref="AuditDetails"/>, or an empty instance on null/invalid input.</returns>
|
||||
public static AuditDetails Deserialize(string? json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
|
||||
@@ -22,12 +22,17 @@ public static class AuditFieldBuilders
|
||||
/// <summary>
|
||||
/// Returns the canonical <c>Action</c> string: <c>"{channel}.{kind}"</c>.
|
||||
/// </summary>
|
||||
/// <param name="channel">The audit channel (e.g. ExternalSystem, Notification).</param>
|
||||
/// <param name="kind">The audit kind (e.g. Sync, Cached, InboundAuthFailure).</param>
|
||||
/// <returns>A dot-separated string in the form <c>"{channel}.{kind}"</c>.</returns>
|
||||
public static string BuildAction(AuditChannel channel, AuditKind kind)
|
||||
=> $"{channel}.{kind}";
|
||||
|
||||
/// <summary>
|
||||
/// Returns the canonical <c>Category</c> string: the channel name.
|
||||
/// </summary>
|
||||
/// <param name="channel">The audit channel whose name becomes the category.</param>
|
||||
/// <returns>The channel enum name as a string (e.g. <c>"ApiOutbound"</c>).</returns>
|
||||
public static string BuildCategory(AuditChannel channel)
|
||||
=> channel.ToString();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ public static class AuditOutcomeProjector
|
||||
/// Projects <paramref name="status"/> + <paramref name="kind"/> onto the canonical
|
||||
/// <see cref="AuditOutcome"/>.
|
||||
/// </summary>
|
||||
/// <param name="status">The audit status of the operation.</param>
|
||||
/// <param name="kind">The audit kind; <see cref="AuditKind.InboundAuthFailure"/> takes precedence over status.</param>
|
||||
/// <returns>The projected <see cref="AuditOutcome"/> value.</returns>
|
||||
public static AuditOutcome Project(AuditStatus status, AuditKind kind)
|
||||
{
|
||||
// Auth-failure kind takes absolute precedence — checked before any status rule.
|
||||
|
||||
@@ -64,6 +64,8 @@ public static class AuditRowProjection
|
||||
/// <see cref="ScadaBridgeAuditEventFactory"/>); a missing/unparseable discriminator
|
||||
/// falls back to the first enum member (defensive — production rows always carry them).
|
||||
/// </summary>
|
||||
/// <param name="evt">The canonical audit event to decompose.</param>
|
||||
/// <returns>An <see cref="AuditRowValues"/> struct containing the typed column values extracted from the event.</returns>
|
||||
public static AuditRowValues Decompose(AuditEvent evt)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(evt);
|
||||
@@ -115,6 +117,8 @@ public static class AuditRowProjection
|
||||
/// are rebuilt via the field builders / outcome projector, and every domain field is
|
||||
/// re-serialized into <c>DetailsJson</c> via <see cref="AuditDetailsCodec"/>.
|
||||
/// </summary>
|
||||
/// <param name="v">The typed column values to recompose into a canonical event.</param>
|
||||
/// <returns>A reconstructed canonical <see cref="AuditEvent"/> with domain fields re-serialized into <c>DetailsJson</c>.</returns>
|
||||
public static AuditEvent Recompose(in AuditRowValues v)
|
||||
{
|
||||
var details = new AuditDetails
|
||||
@@ -163,6 +167,9 @@ public static class AuditRowProjection
|
||||
/// record, so the central ingest paths stamp it here rather than on a top-level
|
||||
/// property as the legacy bespoke record allowed.
|
||||
/// </summary>
|
||||
/// <param name="evt">The canonical audit event to update.</param>
|
||||
/// <param name="ingestedAtUtc">The central-side ingest timestamp to stamp into the event.</param>
|
||||
/// <returns>A new <see cref="AuditEvent"/> with <c>IngestedAtUtc</c> set inside <c>DetailsJson</c>.</returns>
|
||||
public static AuditEvent WithIngestedAtUtc(AuditEvent evt, DateTimeOffset ingestedAtUtc)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(evt);
|
||||
@@ -179,6 +186,10 @@ public static class AuditRowProjection
|
||||
/// or does not match any declared member name — so callers never throw on an
|
||||
/// unknown/renamed enum string (legacy or corrupt rows degrade gracefully).
|
||||
/// </summary>
|
||||
/// <typeparam name="TEnum">The enum type to parse into.</typeparam>
|
||||
/// <param name="value">The string to parse; null or empty triggers the fallback.</param>
|
||||
/// <param name="fallback">Value returned when <paramref name="value"/> is null, empty, or unrecognised.</param>
|
||||
/// <returns>The parsed <typeparamref name="TEnum"/> value, or <paramref name="fallback"/> when the input is null, empty, or unrecognised.</returns>
|
||||
public static TEnum ParseEnum<TEnum>(string? value, TEnum fallback) where TEnum : struct, Enum
|
||||
=> !string.IsNullOrEmpty(value) && Enum.TryParse<TEnum>(value, ignoreCase: false, out var parsed)
|
||||
? parsed
|
||||
@@ -194,6 +205,8 @@ public static class AuditRowProjection
|
||||
public static class AuditEventRowExtensions
|
||||
{
|
||||
/// <summary>Decomposes this canonical record into its typed 24-field view.</summary>
|
||||
/// <param name="evt">The canonical audit event to decompose.</param>
|
||||
/// <returns>An <see cref="AuditRowProjection.AuditRowValues"/> struct with all domain fields extracted from the event.</returns>
|
||||
public static AuditRowProjection.AuditRowValues AsRow(this AuditEvent evt)
|
||||
=> AuditRowProjection.Decompose(evt);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ public static class ScadaBridgeAuditEventFactory
|
||||
/// <param name="payloadTruncated">True when summaries were truncated to the payload cap (DetailsJson).</param>
|
||||
/// <param name="extra">Free-form JSON extension for channel-specific extras (DetailsJson).</param>
|
||||
/// <param name="ingestedAtUtc">UTC ingest timestamp (central-set; DetailsJson).</param>
|
||||
/// <returns>A fully-populated <see cref="AuditEvent"/> with the top-level fields and serialized <c>DetailsJson</c> set.</returns>
|
||||
public static AuditEvent Create(
|
||||
AuditChannel channel,
|
||||
AuditKind kind,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user