Skip to content

Commit b7c5d1b

Browse files
authored
Merge pull request #5 from thiagoluga/pr/abstractions
feat(abstractions): XML docs completos e build multi-target
2 parents 8786229 + deeacc0 commit b7c5d1b

11 files changed

Lines changed: 378 additions & 7 deletions

File tree

Directory.Build.props

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project>
2+
3+
<!-- The shared MSBuild properties live under build/. This root shim is the file
4+
MSBuild auto-discovers when walking up from each project; it forwards to the
5+
real definitions so the props can stay grouped in build/ as per the repo layout. -->
6+
<Import Project="build\Directory.Build.props" />
7+
8+
</Project>

Directory.Packages.props

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<Project>
2+
3+
<!-- Central Package Management lives under build/. This root shim is the file
4+
MSBuild auto-discovers for package versions; it forwards to the real catalog. -->
5+
<Import Project="build\Directory.Packages.props" />
6+
7+
</Project>

NeoReports.sln

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
1+
22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
4-
# Visual Studio 2022
54
VisualStudioVersion = 17.0.31903.59
65
MinimumVisualStudioVersion = 10.0.40607.1
76
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{11111111-1111-1111-1111-111111111111}"
@@ -12,12 +11,35 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks",
1211
EndProject
1312
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{44444444-4444-4444-4444-444444444444}"
1413
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NeoReports.Abstractions", "src\NeoReports.Abstractions\NeoReports.Abstractions.csproj", "{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}"
15+
EndProject
1516
Global
1617
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1718
Debug|Any CPU = Debug|Any CPU
19+
Debug|x64 = Debug|x64
20+
Debug|x86 = Debug|x86
1821
Release|Any CPU = Release|Any CPU
22+
Release|x64 = Release|x64
23+
Release|x86 = Release|x86
24+
EndGlobalSection
25+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
26+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Debug|x64.ActiveCfg = Debug|Any CPU
29+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Debug|x64.Build.0 = Debug|Any CPU
30+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Debug|x86.ActiveCfg = Debug|Any CPU
31+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Debug|x86.Build.0 = Debug|Any CPU
32+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Release|Any CPU.ActiveCfg = Release|Any CPU
33+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Release|Any CPU.Build.0 = Release|Any CPU
34+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Release|x64.ActiveCfg = Release|Any CPU
35+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Release|x64.Build.0 = Release|Any CPU
36+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Release|x86.ActiveCfg = Release|Any CPU
37+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F}.Release|x86.Build.0 = Release|Any CPU
1938
EndGlobalSection
2039
GlobalSection(SolutionProperties) = preSolution
2140
HideSolutionNode = FALSE
2241
EndGlobalSection
42+
GlobalSection(NestedProjects) = preSolution
43+
{7FFF164E-CCD7-448F-8E2A-F0D8F958065F} = {11111111-1111-1111-1111-111111111111}
44+
EndGlobalSection
2345
EndGlobal

src/NeoReports.Abstractions/Exceptions.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ namespace NeoReports.Abstractions;
33
/// <summary>Base exception for all NeoReports errors. Carries a stable machine-readable code.</summary>
44
public class NeoReportsException : Exception
55
{
6+
/// <summary>Creates a NeoReports exception with a stable code.</summary>
7+
/// <param name="code">Stable, machine-readable error code (e.g. "NR-BATCH-001").</param>
8+
/// <param name="message">Human-readable error message.</param>
9+
/// <param name="inner">Optional underlying exception.</param>
610
public NeoReportsException(string code, string message, Exception? inner = null)
711
: base(message, inner) => Code = code;
812

@@ -13,34 +17,51 @@ public NeoReportsException(string code, string message, Exception? inner = null)
1317
/// <summary>A batch failed after exhausting its retries.</summary>
1418
public sealed class BatchFailedException : NeoReportsException
1519
{
20+
/// <summary>Creates an exception describing a batch that failed after all retries.</summary>
21+
/// <param name="pageNumber">Index of the page that failed.</param>
22+
/// <param name="attemptsExhausted">Number of attempts made before giving up.</param>
23+
/// <param name="message">Human-readable error message.</param>
24+
/// <param name="inner">Optional underlying exception.</param>
1625
public BatchFailedException(int pageNumber, int attemptsExhausted, string message, Exception? inner = null)
1726
: base("NR-BATCH-001", message, inner)
1827
{
1928
PageNumber = pageNumber;
2029
AttemptsExhausted = attemptsExhausted;
2130
}
2231

32+
/// <summary>Index of the page that failed.</summary>
2333
public int PageNumber { get; }
34+
35+
/// <summary>Number of attempts made before giving up.</summary>
2436
public int AttemptsExhausted { get; }
2537
}
2638

2739
/// <summary>The source could not be initialized or connected to.</summary>
2840
public sealed class SourceFailedException : NeoReportsException
2941
{
42+
/// <summary>Creates an exception describing a source that could not be used.</summary>
43+
/// <param name="message">Human-readable error message.</param>
44+
/// <param name="inner">Optional underlying exception.</param>
3045
public SourceFailedException(string message, Exception? inner = null)
3146
: base("NR-SOURCE-001", message, inner) { }
3247
}
3348

3449
/// <summary>A failure threshold (consecutive/total/ratio) was exceeded; the report was aborted.</summary>
3550
public sealed class ThresholdExceededException : NeoReportsException
3651
{
52+
/// <summary>Creates an exception describing an exceeded failure threshold.</summary>
53+
/// <param name="message">Human-readable error message.</param>
54+
/// <param name="inner">Optional underlying exception.</param>
3755
public ThresholdExceededException(string message, Exception? inner = null)
3856
: base("NR-THRESHOLD-001", message, inner) { }
3957
}
4058

4159
/// <summary>A report was registered or configured incorrectly.</summary>
4260
public sealed class ConfigurationException : NeoReportsException
4361
{
62+
/// <summary>Creates an exception describing an invalid report configuration.</summary>
63+
/// <param name="message">Human-readable error message.</param>
64+
/// <param name="inner">Optional underlying exception.</param>
4465
public ConfigurationException(string message, Exception? inner = null)
4566
: base("NR-CONFIG-001", message, inner) { }
4667
}

src/NeoReports.Abstractions/Extensibility.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ public interface IWriterFactory
1616
/// <summary>Stable format id (e.g. "csv", "xlsx").</summary>
1717
string Format { get; }
1818

19+
/// <summary>Creates a writer instance.</summary>
20+
/// <param name="options">Format-specific options captured at registration time.</param>
21+
/// <param name="services">The service provider for resolving dependencies.</param>
22+
/// <returns>A new writer.</returns>
1923
IReportWriter Create(IReadOnlyDictionary<string, object?> options, IServiceProvider services);
2024
}
2125

@@ -25,5 +29,9 @@ public interface IDestinationFactory
2529
/// <summary>Stable destination type id (e.g. "local", "s3").</summary>
2630
string Type { get; }
2731

32+
/// <summary>Creates a destination instance.</summary>
33+
/// <param name="options">Destination-specific options captured at registration time.</param>
34+
/// <param name="services">The service provider for resolving dependencies.</param>
35+
/// <returns>A new destination.</returns>
2836
IReportDestination Create(IReadOnlyDictionary<string, object?> options, IServiceProvider services);
2937
}

src/NeoReports.Abstractions/Jobs.cs

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,34 @@ namespace NeoReports.Abstractions;
33
/// <summary>Lifecycle status of a report job.</summary>
44
public enum ReportJobStatus
55
{
6-
Queued, Running, Completed, Failed, Cancelled, Paused, Retrying
6+
/// <summary>Accepted and waiting for a worker.</summary>
7+
Queued,
8+
9+
/// <summary>Currently being processed.</summary>
10+
Running,
11+
12+
/// <summary>Finished successfully (possibly partial when batches were skipped).</summary>
13+
Completed,
14+
15+
/// <summary>Aborted by an unrecoverable error.</summary>
16+
Failed,
17+
18+
/// <summary>Stopped on request before completion.</summary>
19+
Cancelled,
20+
21+
/// <summary>Temporarily suspended (reserved for post-MVP).</summary>
22+
Paused,
23+
24+
/// <summary>Waiting to retry after a transient failure.</summary>
25+
Retrying
726
}
827

928
/// <summary>Aggregate counters for a running/finished job.</summary>
29+
/// <param name="RecordsRead">Total records read from the source.</param>
30+
/// <param name="RecordsWritten">Total records written to outputs.</param>
31+
/// <param name="BytesWritten">Total bytes written across outputs.</param>
32+
/// <param name="Retries">Number of batch retries performed.</param>
33+
/// <param name="BatchesProcessed">Number of batches processed.</param>
1034
public sealed record JobStats(
1135
long RecordsRead = 0,
1236
long RecordsWritten = 0,
@@ -17,6 +41,12 @@ public sealed record JobStats(
1741
/// <summary>A request to enqueue a job for a registered report.</summary>
1842
public sealed class ReportJobRequest
1943
{
44+
/// <summary>Creates a job request for a registered report.</summary>
45+
/// <param name="reportName">Name of the registered report to run.</param>
46+
/// <param name="parameters">Run-time parameters; <c>null</c> is treated as empty.</param>
47+
/// <param name="priority">Relative weight used to route the job.</param>
48+
/// <param name="idempotencyKey">Optional key used to de-duplicate enqueue requests.</param>
49+
/// <param name="requestedBy">Optional identity of the requester.</param>
2050
public ReportJobRequest(
2151
string reportName,
2252
IReadOnlyDictionary<string, object?>? parameters = null,
@@ -31,45 +61,91 @@ public ReportJobRequest(
3161
RequestedBy = requestedBy;
3262
}
3363

64+
/// <summary>Name of the registered report to run.</summary>
3465
public string ReportName { get; }
66+
67+
/// <summary>Run-time parameters for the execution.</summary>
3568
public IReadOnlyDictionary<string, object?> Parameters { get; }
69+
70+
/// <summary>Relative weight used to route the job.</summary>
3671
public JobPriority Priority { get; }
72+
73+
/// <summary>Optional key used to de-duplicate enqueue requests.</summary>
3774
public string? IdempotencyKey { get; }
75+
76+
/// <summary>Optional identity of the requester.</summary>
3877
public string? RequestedBy { get; }
3978
}
4079

4180
/// <summary>Persisted state of a report job.</summary>
4281
public sealed class ReportJob
4382
{
83+
/// <summary>Unique job identifier.</summary>
4484
public required string Id { get; init; }
85+
86+
/// <summary>Name of the report this job runs.</summary>
4587
public required string ReportName { get; init; }
88+
89+
/// <summary>Current lifecycle status.</summary>
4690
public required ReportJobStatus Status { get; init; }
91+
92+
/// <summary>UTC timestamp when the job was created.</summary>
4793
public required DateTimeOffset CreatedAt { get; init; }
94+
95+
/// <summary>UTC timestamp when processing started, if it has.</summary>
4896
public DateTimeOffset? StartedAt { get; init; }
97+
98+
/// <summary>UTC timestamp when the job finished, if it has.</summary>
4999
public DateTimeOffset? CompletedAt { get; init; }
100+
101+
/// <summary>Failure reason when <see cref="Status"/> is <see cref="ReportJobStatus.Failed"/>.</summary>
50102
public string? Error { get; init; }
103+
104+
/// <summary>Aggregate counters for the job.</summary>
51105
public JobStats Stats { get; init; } = new();
106+
107+
/// <summary>Optional identity of the requester.</summary>
52108
public string? RequestedBy { get; init; }
109+
110+
/// <summary>Identifier of the worker that processed (or is processing) the job.</summary>
53111
public string? WorkerId { get; init; }
54112
}
55113

56114
/// <summary>Query filter for listing jobs.</summary>
57115
public sealed record JobQuery
58116
{
117+
/// <summary>Restrict to a single status, or <c>null</c> for any.</summary>
59118
public ReportJobStatus? Status { get; init; }
119+
120+
/// <summary>Restrict to a single report name, or <c>null</c> for any.</summary>
60121
public string? ReportName { get; init; }
122+
123+
/// <summary>Only return jobs created at or after this instant.</summary>
61124
public DateTimeOffset? Since { get; init; }
125+
126+
/// <summary>Maximum number of jobs to return.</summary>
62127
public int Limit { get; init; } = 50;
128+
129+
/// <summary>Number of jobs to skip (paging offset).</summary>
63130
public int Offset { get; init; }
64131
}
65132

66133
/// <summary>Persistence of job state. v1 ships InMemory and a SQL-backed (Hangfire) store.</summary>
67134
public interface IJobStore
68135
{
136+
/// <summary>Creates and persists a new job from a request.</summary>
69137
Task<ReportJob> CreateAsync(ReportJobRequest request, CancellationToken cancellationToken);
138+
139+
/// <summary>Loads a job by id, or returns <c>null</c> when absent.</summary>
70140
Task<ReportJob?> GetAsync(string jobId, CancellationToken cancellationToken);
141+
142+
/// <summary>Updates the status (and optional error) of a job.</summary>
71143
Task UpdateStatusAsync(string jobId, ReportJobStatus status, string? error, CancellationToken cancellationToken);
144+
145+
/// <summary>Updates the aggregate counters of a job.</summary>
72146
Task UpdateStatsAsync(string jobId, JobStats stats, CancellationToken cancellationToken);
147+
148+
/// <summary>Lists jobs matching a query.</summary>
73149
Task<IReadOnlyList<ReportJob>> ListAsync(JobQuery query, CancellationToken cancellationToken);
74150
}
75151

@@ -79,27 +155,44 @@ public interface IJobStore
79155
/// </summary>
80156
public sealed class Checkpoint
81157
{
158+
/// <summary>Identifier of the job this checkpoint belongs to.</summary>
82159
public required string JobId { get; init; }
160+
161+
/// <summary>Index of the last page that completed successfully.</summary>
83162
public int LastCompletedPage { get; init; }
84163

85164
/// <summary>Opaque serializable cursor (see BatchResult{T}.NextCursor).</summary>
86165
public string? LastCursor { get; init; }
166+
167+
/// <summary>Number of records processed up to this checkpoint.</summary>
87168
public long RecordsProcessed { get; init; }
169+
170+
/// <summary>UTC timestamp when this checkpoint was written.</summary>
88171
public DateTimeOffset UpdatedAt { get; init; }
89172
}
90173

91174
/// <summary>Persistence of checkpoints. No-op implementation in v1.</summary>
92175
public interface ICheckpointStore
93176
{
177+
/// <summary>Persists a checkpoint.</summary>
94178
Task SaveAsync(Checkpoint checkpoint, CancellationToken cancellationToken);
179+
180+
/// <summary>Loads the latest checkpoint for a job, or <c>null</c> when absent.</summary>
95181
Task<Checkpoint?> LoadAsync(string jobId, CancellationToken cancellationToken);
182+
183+
/// <summary>Deletes any checkpoint for a job.</summary>
96184
Task DeleteAsync(string jobId, CancellationToken cancellationToken);
97185
}
98186

99187
/// <summary>Enqueues and tracks report jobs. v1: single vertical worker (Hangfire single-server).</summary>
100188
public interface IReportJobScheduler
101189
{
190+
/// <summary>Enqueues a job and returns its id.</summary>
102191
Task<string> EnqueueAsync(ReportJobRequest request, CancellationToken cancellationToken);
192+
193+
/// <summary>Loads a job by id, or returns <c>null</c> when absent.</summary>
103194
Task<ReportJob?> GetAsync(string jobId, CancellationToken cancellationToken);
195+
196+
/// <summary>Requests cooperative cancellation of a job; returns whether it was accepted.</summary>
104197
Task<bool> CancelAsync(string jobId, CancellationToken cancellationToken);
105198
}

0 commit comments

Comments
 (0)