Skip to content

Commit ce9922b

Browse files
authored
Merge pull request #105 from Akash29g/docs/xml-doc-comments
docs: XML documentation pass across Domain, Service & API public surface
2 parents ddb8fe4 + a230e78 commit ce9922b

138 files changed

Lines changed: 1722 additions & 21 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Directory.Build.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<Project>
22
<!-- Coverage gate: applies only to *.Tests projects -->
33
<PropertyGroup Condition="$(MSBuildProjectName.EndsWith('.Tests'))">
4+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
5+
<!-- Keep the build green while you document incrementally:
6+
CS1591 = "missing XML comment on public member" -->
7+
<NoWarn>$(NoWarn);CS1591</NoWarn>
48
<Threshold>30</Threshold>
59
<ThresholdType>line</ThresholdType>
610
<ThresholdStat>total</ThresholdStat>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
namespace DocAnalytics.Api.Auth;
22

3+
/// <summary>Strongly-typed JWT configuration bound from the "Jwt" settings section.</summary>
34
public class JwtSettings
45
{
6+
/// <summary>The token issuer (iss).</summary>
57
public string Issuer { get; set; } = null!;
8+
/// <summary>The intended token audience (aud).</summary>
69
public string Audience { get; set; } = null!;
10+
/// <summary>The symmetric signing key.</summary>
711
public string Key { get; set; } = null!;
12+
/// <summary>Access-token lifetime in minutes.</summary>
813
public int ExpiryMinutes { get; set; } = 120;
914
}

DocAnalytics.Api/BackgroundServices/AlertEvaluationBackgroundService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,25 @@
44

55
namespace DocAnalytics.Api.BackgroundServices;
66

7+
/// <summary>Hosted service that periodically evaluates all alert rules (once per minute) via a scoped <see cref="IAlertEvaluator"/>.</summary>
78
[ExcludeFromCodeCoverage]
89
public sealed class AlertEvaluationBackgroundService : BackgroundService
910
{
1011
private readonly IServiceScopeFactory _scopes;
1112
private readonly ILogger<AlertEvaluationBackgroundService> _logger;
1213
private static readonly TimeSpan Interval = TimeSpan.FromMinutes(1);
1314

15+
/// <summary>Creates the background service with a scope factory and logger.</summary>
16+
/// <param name="scopes">Factory used to create a DI scope per tick.</param>
17+
/// <param name="logger">The logger.</param>
1418
public AlertEvaluationBackgroundService(
1519
IServiceScopeFactory scopes, ILogger<AlertEvaluationBackgroundService> logger)
1620
{
1721
_scopes = scopes;
1822
_logger = logger;
1923
}
2024

25+
/// <inheritdoc />
2126
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
2227
{
2328
using var timer = new PeriodicTimer(Interval);

DocAnalytics.Api/BackgroundServices/ExtractionWorker.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,28 @@
1111

1212
namespace DocAnalytics.Api.BackgroundServices;
1313

14+
/// <summary>
15+
/// Hosted worker that drains the extraction queue and runs the invoice pipeline per file:
16+
/// download → malware/format security gates → Bedrock extraction → validation → persist header/line items,
17+
/// updating file/batch state and broadcasting real-time notifications throughout.
18+
/// </summary>
1419
[ExcludeFromCodeCoverage]
1520
public sealed class ExtractionWorker : BackgroundService
1621
{
1722
private readonly IExtractionQueue _queue;
1823
private readonly IServiceScopeFactory _scopes;
1924
private readonly ILogger<ExtractionWorker> _logger;
2025

26+
/// <summary>Creates the worker with the shared queue, a scope factory, and a logger.</summary>
27+
/// <param name="queue">The extraction queue to consume.</param>
28+
/// <param name="scopes">Factory used to create a DI scope per file.</param>
29+
/// <param name="logger">The logger.</param>
2130
public ExtractionWorker(IExtractionQueue queue, IServiceScopeFactory scopes, ILogger<ExtractionWorker> logger)
2231
{
2332
_queue = queue; _scopes = scopes; _logger = logger;
2433
}
2534

35+
/// <inheritdoc />
2636
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
2737
{
2838
await foreach (var fileId in _queue.DequeueAllAsync(stoppingToken))
@@ -32,6 +42,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
3242
}
3343
}
3444

45+
/// <summary>Runs the full extraction pipeline for a single file within its own DI scope.</summary>
3546
private async Task ProcessAsync(Guid fileId, CancellationToken ct)
3647
{
3748
using var scope = _scopes.CreateScope();
@@ -247,6 +258,7 @@ await notifier.NotifyFileStateChangedAsync(file.SiteId,
247258
}
248259
}
249260

261+
/// <summary>Marks a file (and its parent batch counters) as failed with the given error, logs it, and broadcasts the change.</summary>
250262
private static async Task FailFileAsync(
251263
AppDbContext db, IPipelineNotifier notifier,
252264
FileRecord file, Transaction txn, DateTime startedAt,
@@ -294,6 +306,7 @@ await notifier.NotifyFileStateChangedAsync(file.SiteId,
294306
}
295307

296308

309+
/// <summary>Recomputes the batch state (DT-1): any failure marks the batch Failed, but only once every file is settled.</summary>
297310
// DT-1 preserved: any file fails → batch Failed. But only finalize once ALL files are settled.
298311
private static void RecomputeState(Transaction t, DateTime at)
299312
{
Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,56 @@
11
namespace DocAnalytics.Api.Common;
22

3+
/// <summary>Standard response envelope wrapping a payload, optional paging metadata, and an optional error.</summary>
4+
/// <typeparam name="T">The payload type.</typeparam>
35
public class ApiResponse<T>
46
{
7+
/// <summary>The payload, when the request succeeded.</summary>
58
public T? Data { get; set; }
9+
/// <summary>Paging metadata, for list responses.</summary>
610
public Meta? Meta { get; set; }
11+
/// <summary>Error details, when the request failed.</summary>
712
public ApiError? Error { get; set; }
813

14+
/// <summary>Creates a success envelope for a single payload.</summary>
15+
/// <param name="data">The payload.</param>
16+
/// <returns>The envelope.</returns>
917
public static ApiResponse<T> Ok(T data) => new() { Data = data };
18+
19+
/// <summary>Creates a success envelope for a list payload with paging metadata.</summary>
20+
/// <param name="data">The payload.</param>
21+
/// <param name="meta">The paging metadata.</param>
22+
/// <returns>The envelope.</returns>
1023
public static ApiResponse<T> OkList(T data, Meta meta) => new() { Data = data, Meta = meta };
24+
25+
/// <summary>Creates a failure envelope with an error code, message, and optional details.</summary>
26+
/// <param name="code">The machine-readable error code.</param>
27+
/// <param name="msg">The human-readable message.</param>
28+
/// <param name="details">Optional structured error details.</param>
29+
/// <returns>The envelope.</returns>
1130
public static ApiResponse<T> Fail(string code, string msg, object? details = null)
1231
=> new() { Error = new ApiError { Code = code, Message = msg, Details = details } };
1332
}
14-
public class Meta { public int TotalCount { get; set; } public int Page { get; set; } public int PageSize { get; set; } public int TotalPages { get; set; } }
15-
public class ApiError { public string Code { get; set; } = null!; public string Message { get; set; } = null!; public object? Details { get; set; } }
33+
34+
/// <summary>Paging metadata for list responses.</summary>
35+
public class Meta
36+
{
37+
/// <summary>Total rows across all pages.</summary>
38+
public int TotalCount { get; set; }
39+
/// <summary>The 1-based page number.</summary>
40+
public int Page { get; set; }
41+
/// <summary>Rows per page.</summary>
42+
public int PageSize { get; set; }
43+
/// <summary>Total number of pages.</summary>
44+
public int TotalPages { get; set; }
45+
}
46+
47+
/// <summary>Structured error information returned in a failure envelope.</summary>
48+
public class ApiError
49+
{
50+
/// <summary>Machine-readable error code.</summary>
51+
public string Code { get; set; } = null!;
52+
/// <summary>Human-readable error message.</summary>
53+
public string Message { get; set; } = null!;
54+
/// <summary>Optional structured details (e.g. validation errors).</summary>
55+
public object? Details { get; set; }
56+
}

DocAnalytics.Api/Common/BaseController.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,21 @@
22

33
namespace DocAnalytics.Api.Common;
44

5+
/// <summary>Base API controller providing helpers to wrap results in the standard <see cref="ApiResponse{T}"/> envelope.</summary>
56
[ApiController]
67
[Route("api/v1/[controller]")]
78
public abstract class BaseController : ControllerBase
89
{
10+
/// <summary>Wraps a single payload in a success envelope.</summary>
11+
/// <typeparam name="T">The payload type.</typeparam>
12+
/// <param name="data">The payload.</param>
13+
/// <returns>A 200 OK result with the wrapped payload.</returns>
914
protected IActionResult Envelope<T>(T data) => Ok(ApiResponse<T>.Ok(data));
15+
16+
/// <summary>Wraps a list payload plus paging metadata in a success envelope.</summary>
17+
/// <typeparam name="T">The payload type.</typeparam>
18+
/// <param name="data">The payload.</param>
19+
/// <param name="meta">The paging metadata.</param>
20+
/// <returns>A 200 OK result with the wrapped payload and metadata.</returns>
1021
protected IActionResult EnvelopeList<T>(T data, Meta meta) => Ok(ApiResponse<T>.OkList(data, meta));
1122
}

DocAnalytics.Api/Common/CurrentUser.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,25 @@
22

33
namespace DocAnalytics.Api.Common;
44

5+
/// <summary>Request-scoped <see cref="ICurrentUser"/> implementation, populated from JWT claims by the tenant/site middleware.</summary>
56
public class CurrentUser : ICurrentUser
67
{
8+
/// <inheritdoc />
79
public Guid UserId { get; private set; }
10+
/// <inheritdoc />
811
public Guid TenantId { get; private set; }
12+
/// <inheritdoc />
913
public Guid SiteId { get; private set; }
14+
/// <inheritdoc />
1015
public string Role { get; private set; } = string.Empty;
16+
/// <inheritdoc />
1117
public bool IsAuthenticated { get; private set; }
1218

19+
/// <summary>Populates the identity/tenancy context for the current request and marks it authenticated.</summary>
20+
/// <param name="userId">The authenticated user's id.</param>
21+
/// <param name="tenantId">The active tenant id.</param>
22+
/// <param name="siteId">The active site id.</param>
23+
/// <param name="role">The user's role.</param>
1324
public void Set(Guid userId, Guid tenantId, Guid siteId, string role)
1425
{
1526
UserId = userId; TenantId = tenantId; SiteId = siteId; Role = role; IsAuthenticated = true;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
namespace DocAnalytics.Api.Configuration;
22

3+
/// <summary>Rate-limiting configuration bound from the "RateLimiting" section (Round 5).</summary>
34
public sealed class RateLimitOptions
45
{
6+
/// <summary>The configuration section name.</summary>
57
public const string SectionName = "RateLimiting";
68

9+
/// <summary>Master on/off switch for rate limiting.</summary>
710
public bool Enabled { get; set; } = true;
11+
/// <summary>Limits for the login policy (per client IP).</summary>
812
public RateLimitPolicySettings Login { get; set; } = new() { PermitLimit = 5, WindowSeconds = 60 };
13+
/// <summary>Limits for read endpoints (per authenticated user).</summary>
914
public RateLimitPolicySettings Reads { get; set; } = new() { PermitLimit = 100, WindowSeconds = 60 };
15+
/// <summary>Limits for export endpoints (tight, per user).</summary>
1016
public RateLimitPolicySettings Export { get; set; } = new() { PermitLimit = 3, WindowSeconds = 60 };
1117
}
1218

19+
/// <summary>Fixed-window limit settings for a single rate-limit policy.</summary>
1320
public sealed class RateLimitPolicySettings
1421
{
22+
/// <summary>Maximum requests permitted per window.</summary>
1523
public int PermitLimit { get; set; }
24+
/// <summary>Window length in seconds.</summary>
1625
public int WindowSeconds { get; set; }
26+
/// <summary>Queue length for waiting requests; 0 rejects immediately (no queueing).</summary>
1727
public int QueueLimit { get; set; } = 0; // 0 = reject immediately, no queueing
1828
}

DocAnalytics.Api/Configuration/RateLimitingExtensions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@
44

55
namespace DocAnalytics.Api.Configuration;
66

7+
/// <summary>Registers the ASP.NET Core rate limiter with login/reads/export policies (Round 5) and a standard 429 envelope.</summary>
78
public static class RateLimitingExtensions
89
{
10+
/// <summary>Policy name for login endpoints (partitioned by client IP).</summary>
911
public const string LoginPolicy = "login";
12+
/// <summary>Policy name for read endpoints (partitioned by authenticated user).</summary>
1013
public const string ReadsPolicy = "reads";
14+
/// <summary>Policy name for export endpoints (tight, partitioned by user).</summary>
1115
public const string ExportPolicy = "export";
1216

17+
/// <summary>Configures the rate limiter, its policies, and the 429 rejection response.</summary>
18+
/// <param name="services">The service collection.</param>
19+
/// <param name="config">The application configuration (reads the "RateLimiting" section).</param>
20+
/// <returns>The same service collection, for chaining.</returns>
1321
public static IServiceCollection AddRateLimitingFeature(
1422
this IServiceCollection services, IConfiguration config)
1523
{

DocAnalytics.Api/Configuration/SecurityFoundationExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace DocAnalytics.Api.Configuration;
66

7+
/// <summary>Round 0 security foundation wiring: binds <see cref="SecurityOptions"/> and registers CORS, HSTS, and forwarded-headers services.</summary>
78
[ExcludeFromCodeCoverage]
89
public static class SecurityFoundationExtensions
910
{

0 commit comments

Comments
 (0)