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:
Joseph Doherty
2026-06-03 11:39:32 -04:00
parent a050170414
commit eabf270d71
208 changed files with 867 additions and 114 deletions
@@ -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);
}
@@ -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);
}
@@ -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 (01000).</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,
@@ -13,15 +13,18 @@ public sealed record ValidationResult
public IReadOnlyList<ValidationEntry> Warnings { get; init; } = [];
/// <summary>Returns a result with no errors or warnings.</summary>
/// <returns>A <see cref="ValidationResult"/> with empty error and warning lists.</returns>
public static ValidationResult Success() => new();
/// <summary>Returns a result containing the given errors.</summary>
/// <param name="errors">The validation errors to include.</param>
/// <returns>A <see cref="ValidationResult"/> whose <see cref="Errors"/> list contains the supplied entries.</returns>
public static ValidationResult FromErrors(params ValidationEntry[] errors) =>
new() { Errors = errors };
/// <summary>Merges multiple validation results into a single combined result.</summary>
/// <param name="results">The results to merge.</param>
/// <returns>A new <see cref="ValidationResult"/> with all errors and warnings from every supplied result combined.</returns>
public static ValidationResult Merge(params ValidationResult[] results)
{
var errors = new List<ValidationEntry>();
@@ -54,6 +57,7 @@ public sealed record ValidationEntry
/// <param name="category">The validation category.</param>
/// <param name="message">The error message.</param>
/// <param name="entityName">The canonical name of the entity that caused the error, if any.</param>
/// <returns>A new <see cref="ValidationEntry"/> representing an error.</returns>
public static ValidationEntry Error(ValidationCategory category, string message, string? entityName = null) =>
new() { Category = category, Message = message, EntityName = entityName };
@@ -61,6 +65,7 @@ public sealed record ValidationEntry
/// <param name="category">The validation category.</param>
/// <param name="message">The warning message.</param>
/// <param name="entityName">The canonical name of the entity that triggered the warning, if any.</param>
/// <returns>A new <see cref="ValidationEntry"/> representing a warning.</returns>
public static ValidationEntry Warning(ValidationCategory category, string message, string? entityName = null) =>
new() { Category = category, Message = message, EntityName = entityName };
}
@@ -49,6 +49,7 @@ public sealed class Result<T>
/// <param name="error">Non-blank error message describing the failure.</param>
/// <exception cref="ArgumentNullException"><paramref name="error"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="error"/> is empty or whitespace.</exception>
/// <returns>A failed <see cref="Result{T}"/> carrying the error message.</returns>
public static Result<T> Failure(string error) => new(error);
/// <summary>Pattern-matches the result, invoking either the success or failure delegate.</summary>
@@ -22,6 +22,7 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Types;
public readonly record struct TrackedOperationId(Guid Value)
{
/// <summary>Mint a fresh id at the call site (script-thread safe).</summary>
/// <returns>A new <see cref="TrackedOperationId"/> wrapping a freshly generated GUID.</returns>
public static TrackedOperationId New() => new(Guid.NewGuid());
/// <summary>
@@ -30,6 +31,7 @@ public readonly record struct TrackedOperationId(Guid Value)
/// <see cref="TryParse"/> instead.
/// </summary>
/// <param name="s">GUID string to parse.</param>
/// <returns>A <see cref="TrackedOperationId"/> wrapping the parsed GUID.</returns>
public static TrackedOperationId Parse(string s) => new(Guid.Parse(s));
/// <summary>
@@ -39,6 +41,7 @@ public readonly record struct TrackedOperationId(Guid Value)
/// </summary>
/// <param name="s">GUID string to parse, or null.</param>
/// <param name="result">Parsed value on success; default on failure.</param>
/// <returns><c>true</c> if the string was successfully parsed; otherwise <c>false</c>.</returns>
public static bool TryParse(string? s, out TrackedOperationId result)
{
if (Guid.TryParse(s, out var g))
@@ -14,6 +14,7 @@ public static class ValueFormatter
/// scalars and comma-separated elements for array/collection types.
/// </summary>
/// <param name="value">The value to format; null returns an empty string.</param>
/// <returns>The culture-invariant string representation of the value, or an empty string if <paramref name="value"/> is <c>null</c>.</returns>
/// <remarks>
/// Formatting is <see cref="CultureInfo.InvariantCulture">culture-invariant</see>:
/// numbers and <see cref="DateTime"/> values render the same regardless of the
@@ -16,6 +16,7 @@ public static class MxGatewayEndpointConfigValidator
/// </summary>
/// <param name="config">The MxGateway endpoint configuration to validate.</param>
/// <param name="fieldPrefix">Optional prefix prepended to each field name in error entries (e.g., "Primary.").</param>
/// <returns>A <see cref="ValidationResult"/> with errors for any invalid fields, or a success result when all fields are valid.</returns>
public static ValidationResult Validate(MxGatewayEndpointConfig config, string fieldPrefix = "")
{
var errors = new List<ValidationEntry>();
@@ -16,6 +16,7 @@ public static class OpcUaEndpointConfigValidator
/// </summary>
/// <param name="config">The OPC UA endpoint configuration to validate.</param>
/// <param name="fieldPrefix">Optional prefix prepended to each field name in error entries (e.g., "Primary.").</param>
/// <returns>A <see cref="ValidationResult"/> containing field-level errors, or success if all fields are valid.</returns>
public static ValidationResult Validate(OpcUaEndpointConfig config, string fieldPrefix = "")
{
var errors = new List<ValidationEntry>();