Skip to content

Commit b71ac13

Browse files
2 parents 2780fe3 + 0a449e3 commit b71ac13

File tree

58 files changed

+1605
-357
lines changed

Some content is hidden

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

58 files changed

+1605
-357
lines changed

Core/Application/Common/Behaviors/CachingBehavior.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace SecureCleanApiWaf.Core.Application.Common.Behaviors
3030
///
3131
/// In a CQRS (Command Query Responsibility Segregation) architecture, MediatR is often used to dispatch commands (for state changes)
3232
/// and queries (for data retrieval) to their respective handlers. This caching behavior is especially useful for queries, as it can
33-
/// intercept query requests, check if the result is already cached, and return the cached data if availablereducing load on the system
33+
/// intercept query requests, check if the result is already cached, and return the cached data if availablereducing load on the system
3434
/// and improving performance. For commands, which change state, caching is typically bypassed. This ensures that queries are fast and
3535
/// scalable, while commands remain consistent and reliable, fully supporting the separation of read and write concerns central to CQRS.
3636
/// </summary>
@@ -41,6 +41,13 @@ IDistributedCache cache
4141
: IPipelineBehavior<TRequest, TResponse>
4242
where TRequest : ICacheable
4343
{
44+
/// <summary>
45+
/// Intercepts cacheable requests to return a cached response when available or invoke the handler, cache its result, and return that response.
46+
/// </summary>
47+
/// <param name="request">The cacheable request that provides the cache key and controls behavior (set BypassCache to skip caching; may specify SlidingExpirationInMinutes and AbsoluteExpirationInMinutes).</param>
48+
/// <param name="next">The handler delegate to execute when a cached response is not available or caching is bypassed.</param>
49+
/// <param name="cancellationToken">Cancellation token used for cache operations and handler execution.</param>
50+
/// <returns>The cached response if present for the request's CacheKey; otherwise the response produced by invoking the handler.</returns>
4451
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
4552
{
4653
TResponse response;
@@ -98,4 +105,4 @@ async Task<TResponse> GetResponseAndAddToCache()
98105
return response;
99106
}
100107
}
101-
}
108+
}

Core/Application/Common/DTOs/TokenBlacklistStatusDto.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,14 @@ public class TokenBlacklistStatusDto
4848

4949
/// <summary>
5050
/// Creates a blacklisted token status.
51+
/// <summary>
52+
/// Create a TokenBlacklistStatusDto representing a token that has been blacklisted.
5153
/// </summary>
54+
/// <param name="tokenId">The token's JWT ID (jti), or null if unknown.</param>
55+
/// <param name="blacklistedAt">The UTC time when the token was blacklisted, or null if not recorded.</param>
56+
/// <param name="tokenExpiresAt">The token's natural expiration time, or null if unknown.</param>
57+
/// <param name="fromCache">Whether the status was retrieved from cache.</param>
58+
/// <returns>A TokenBlacklistStatusDto with IsBlacklisted set to `true`, Status set to "blacklisted", Details describing the blacklist, CheckedAt set to the current UTC time, and FromCache set to the provided value.</returns>
5259
public static TokenBlacklistStatusDto Blacklisted(
5360
string? tokenId,
5461
DateTime? blacklistedAt,
@@ -70,7 +77,13 @@ public static TokenBlacklistStatusDto Blacklisted(
7077

7178
/// <summary>
7279
/// Creates a valid (not blacklisted) token status.
80+
/// <summary>
81+
/// Creates a TokenBlacklistStatusDto representing a valid (not blacklisted) token.
7382
/// </summary>
83+
/// <param name="tokenId">The token's JWT ID (jti), or null if unavailable.</param>
84+
/// <param name="tokenExpiresAt">The token's natural expiration time, or null if unknown.</param>
85+
/// <param name="fromCache">Whether the result was retrieved from cache.</param>
86+
/// <returns>A DTO indicating the token is valid; CheckedAt is set to the current UTC time.</returns>
7487
public static TokenBlacklistStatusDto Valid(
7588
string? tokenId,
7689
DateTime? tokenExpiresAt,
@@ -90,7 +103,11 @@ public static TokenBlacklistStatusDto Valid(
90103

91104
/// <summary>
92105
/// Creates an invalid token status (malformed or expired).
106+
/// <summary>
107+
/// Create a TokenBlacklistStatusDto representing an invalid token status.
93108
/// </summary>
109+
/// <param name="reason">Human-readable explanation for why the token is considered invalid.</param>
110+
/// <returns>A TokenBlacklistStatusDto with IsBlacklisted set to false, Status set to "invalid", Details set to the provided reason, CheckedAt set to the current UTC time, and FromCache set to false.</returns>
94111
public static TokenBlacklistStatusDto Invalid(string reason)
95112
{
96113
return new TokenBlacklistStatusDto
@@ -103,4 +120,4 @@ public static TokenBlacklistStatusDto Invalid(string reason)
103120
};
104121
}
105122
}
106-
}
123+
}

Core/Application/Common/Interfaces/IApiDataItemRepository.cs

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ public interface IApiDataItemRepository
6363
/// </summary>
6464
/// <param name="id">The item's unique identifier.</param>
6565
/// <param name="cancellationToken">Cancellation token.</param>
66-
/// <returns>The item if found, null otherwise.</returns>
66+
/// <summary>
67+
/// Retrieves an ApiDataItem by its unique identifier.
68+
/// </summary>
69+
/// <param name="id">The unique identifier of the ApiDataItem to retrieve.</param>
70+
/// <returns>The ApiDataItem with the specified id, or `null` if no matching item is found.</returns>
6771
Task<ApiDataItem?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
6872

6973
/// <summary>
@@ -76,7 +80,11 @@ public interface IApiDataItemRepository
7680
/// Used to find existing items when synchronizing from external APIs.
7781
/// Prevents duplicate storage of the same external data.
7882
/// Should be optimized with database index on ExternalId.
79-
/// </remarks>
83+
/// <summary>
84+
/// Retrieves an <see cref="ApiDataItem"/> that matches the specified external system identifier.
85+
/// </summary>
86+
/// <param name="externalId">The identifier assigned to the item by an external system.</param>
87+
/// <returns>The matching <see cref="ApiDataItem"/>, or <c>null</c> if no match exists.</returns>
8088
Task<ApiDataItem?> GetByExternalIdAsync(string externalId, CancellationToken cancellationToken = default);
8189

8290
/// <summary>
@@ -87,7 +95,10 @@ public interface IApiDataItemRepository
8795
/// <remarks>
8896
/// Returns only items with Active status.
8997
/// Used for serving fresh data to API consumers.
90-
/// </remarks>
98+
/// <summary>
99+
/// Retrieves all active API data items — items that are not marked as deleted and are not marked as stale.
100+
/// </summary>
101+
/// <returns>A read-only list of active <see cref="ApiDataItem"/> instances.</returns>
91102
Task<IReadOnlyList<ApiDataItem>> GetActiveItemsAsync(CancellationToken cancellationToken = default);
92103

93104
/// <summary>
@@ -99,7 +110,10 @@ public interface IApiDataItemRepository
99110
/// Includes active, stale, and deleted items.
100111
/// Used for administrative purposes and full data exports.
101112
/// Consider pagination for large datasets.
102-
/// </remarks>
113+
/// <summary>
114+
/// Retrieves all API data items regardless of their status (including deleted or stale items).
115+
/// </summary>
116+
/// <returns>A read-only list containing every <see cref="ApiDataItem"/> in the repository.</returns>
103117
Task<IReadOnlyList<ApiDataItem>> GetAllItemsAsync(CancellationToken cancellationToken = default);
104118

105119
/// <summary>
@@ -111,7 +125,12 @@ public interface IApiDataItemRepository
111125
/// <remarks>
112126
/// Useful for batch operations like refreshing stale items
113127
/// or cleaning up deleted items.
114-
/// </remarks>
128+
/// <summary>
129+
/// Retrieves all ApiDataItem instances that have the specified data status.
130+
/// </summary>
131+
/// <param name="status">The DataStatus value to filter items by.</param>
132+
/// <param name="cancellationToken">Token to observe while waiting for the task to complete.</param>
133+
/// <returns>A read-only list of ApiDataItem objects whose status equals the specified value.</returns>
115134
Task<IReadOnlyList<ApiDataItem>> GetItemsByStatusAsync(DataStatus status, CancellationToken cancellationToken = default);
116135

117136
/// <summary>
@@ -124,7 +143,11 @@ public interface IApiDataItemRepository
124143
/// Returns items where (UtcNow - LastSyncedAt) > maxAge.
125144
/// Used by background refresh jobs to identify stale data.
126145
/// Should be optimized with index on LastSyncedAt.
127-
/// </remarks>
146+
/// <summary>
147+
/// Finds API data items whose last synchronization time is older than the provided maximum age and therefore need refreshing.
148+
/// </summary>
149+
/// <param name="maxAge">The maximum allowed age since an item's LastSyncedAt; items older than this value are considered to need refresh.</param>
150+
/// <returns>A read-only list of ApiDataItem instances whose LastSyncedAt age exceeds <paramref name="maxAge"/>.</returns>
128151
Task<IReadOnlyList<ApiDataItem>> GetItemsNeedingRefreshAsync(TimeSpan maxAge, CancellationToken cancellationToken = default);
129152

130153
/// <summary>
@@ -136,7 +159,11 @@ public interface IApiDataItemRepository
136159
/// <remarks>
137160
/// Useful for managing data from multiple API sources.
138161
/// Enables source-specific refresh or cleanup operations.
139-
/// </remarks>
162+
/// <summary>
163+
/// Retrieves API data items that originate from the specified source URL.
164+
/// </summary>
165+
/// <param name="sourceUrl">The source URL to filter items by.</param>
166+
/// <returns>A read-only list of <see cref="ApiDataItem"/> instances that originate from the specified source URL.</returns>
140167
Task<IReadOnlyList<ApiDataItem>> GetItemsBySourceUrlAsync(string sourceUrl, CancellationToken cancellationToken = default);
141168

142169
/// <summary>
@@ -149,7 +176,11 @@ public interface IApiDataItemRepository
149176
/// Performs case-insensitive partial match on Name field.
150177
/// Useful for search functionality in API endpoints.
151178
/// Consider adding pagination for large result sets.
152-
/// </remarks>
179+
/// <summary>
180+
/// Finds ApiDataItem objects whose names match a case-insensitive partial search term.
181+
/// </summary>
182+
/// <param name="searchTerm">The substring to match against item names; matching is case-insensitive and treats the term as a partial search.</param>
183+
/// <returns>A read-only list of ApiDataItem instances whose names match the provided search term.</returns>
153184
Task<IReadOnlyList<ApiDataItem>> SearchByNameAsync(string searchTerm, CancellationToken cancellationToken = default);
154185

155186
/// <summary>
@@ -162,7 +193,12 @@ public interface IApiDataItemRepository
162193
/// <remarks>
163194
/// Searches JSON metadata field for specific keys/values.
164195
/// Implementation depends on database JSON support.
165-
/// </remarks>
196+
/// <summary>
197+
/// Retrieves ApiDataItem entities that contain a specified metadata key, optionally filtered to a specific metadata value.
198+
/// </summary>
199+
/// <param name="metadataKey">The metadata key to match.</param>
200+
/// <param name="metadataValue">An optional metadata value to match; when null, items containing the key are returned regardless of the value.</param>
201+
/// <returns>A read-only list of ApiDataItem objects matching the metadata criteria.</returns>
166202
Task<IReadOnlyList<ApiDataItem>> GetItemsByMetadataAsync(string metadataKey, object? metadataValue = null, CancellationToken cancellationToken = default);
167203

168204
/// <summary>
@@ -174,7 +210,11 @@ public interface IApiDataItemRepository
174210
/// <remarks>
175211
/// Used to prevent duplicate storage of external data.
176212
/// Should check against non-deleted items only.
177-
/// </remarks>
213+
/// <summary>
214+
/// Checks whether a non-deleted ApiDataItem with the specified external system identifier exists.
215+
/// </summary>
216+
/// <param name="externalId">The external system identifier to check for.</param>
217+
/// <returns>`true` if a non-deleted item with the given external ID exists, `false` otherwise.</returns>
178218
Task<bool> ExistsAsync(string externalId, CancellationToken cancellationToken = default);
179219

180220
/// <summary>
@@ -186,7 +226,10 @@ public interface IApiDataItemRepository
186226
/// <remarks>
187227
/// Creates a new record in the database.
188228
/// Typically called after fetching data from external API.
189-
/// </remarks>
229+
/// <summary>
230+
/// Adds a new ApiDataItem to the repository.
231+
/// </summary>
232+
/// <param name="item">The ApiDataItem to add; must not be null. The item will be tracked and persisted when SaveChangesAsync is called.</param>
190233
Task AddAsync(ApiDataItem item, CancellationToken cancellationToken = default);
191234

192235
/// <summary>
@@ -198,7 +241,11 @@ public interface IApiDataItemRepository
198241
/// <remarks>
199242
/// Optimized batch operation for bulk imports.
200243
/// More efficient than adding one by one.
201-
/// </remarks>
244+
/// <summary>
245+
/// Adds multiple ApiDataItem instances to the repository.
246+
/// </summary>
247+
/// <param name="items">The collection of ApiDataItem objects to add.</param>
248+
/// <param name="cancellationToken">A token to cancel the operation.</param>
202249
Task AddRangeAsync(IEnumerable<ApiDataItem> items, CancellationToken cancellationToken = default);
203250

204251
/// <summary>
@@ -210,7 +257,11 @@ public interface IApiDataItemRepository
210257
/// <remarks>
211258
/// Updates item data, status, metadata, etc.
212259
/// Called after refreshing data from external API.
213-
/// </remarks>
260+
/// <summary>
261+
/// Applies the provided ApiDataItem's updated values to the repository state.
262+
/// </summary>
263+
/// <param name="item">The ApiDataItem containing the updated data to store.</param>
264+
/// <param name="cancellationToken">Token to cancel the operation.</param>
214265
Task UpdateAsync(ApiDataItem item, CancellationToken cancellationToken = default);
215266

216267
/// <summary>
@@ -222,7 +273,12 @@ public interface IApiDataItemRepository
222273
/// <remarks>
223274
/// Optimized batch operation for bulk updates.
224275
/// Useful for background refresh jobs.
225-
/// </remarks>
276+
/// <summary>
277+
/// Updates multiple ApiDataItem entities in a single batch operation.
278+
/// </summary>
279+
/// <param name="items">The collection of items to update.</param>
280+
/// <param name="cancellationToken">Token to observe for cancellation.</param>
281+
/// <returns>The number of items updated.</returns>
226282
Task<int> UpdateRangeAsync(IEnumerable<ApiDataItem> items, CancellationToken cancellationToken = default);
227283

228284
/// <summary>
@@ -234,7 +290,10 @@ public interface IApiDataItemRepository
234290
/// <remarks>
235291
/// Soft delete - marks item as deleted but preserves data.
236292
/// Use item.MarkAsDeleted() before calling this.
237-
/// </remarks>
293+
/// <summary>
294+
/// Marks the given ApiDataItem as deleted in the repository (soft delete) without removing its data.
295+
/// </summary>
296+
/// <param name="item">The ApiDataItem to soft-delete; the item is expected to be marked as deleted prior to calling this method.</param>
238297
Task DeleteAsync(ApiDataItem item, CancellationToken cancellationToken = default);
239298

240299
/// <summary>
@@ -247,7 +306,12 @@ public interface IApiDataItemRepository
247306
/// Hard delete for cleanup of old deleted items.
248307
/// Should be used carefully, typically by scheduled cleanup jobs.
249308
/// Permanently removes data - cannot be recovered.
250-
/// </remarks>
309+
/// <summary>
310+
/// Permanently removes ApiDataItem records that were soft-deleted prior to the specified cutoff date.
311+
/// </summary>
312+
/// <param name="olderThan">Remove items soft-deleted before this date and time (UTC is recommended).</param>
313+
/// <param name="cancellationToken">Token to cancel the operation.</param>
314+
/// <returns>The number of items that were permanently removed.</returns>
251315
Task<int> PermanentlyDeleteOldItemsAsync(DateTime olderThan, CancellationToken cancellationToken = default);
252316

253317
/// <summary>
@@ -259,7 +323,12 @@ public interface IApiDataItemRepository
259323
/// <remarks>
260324
/// Bulk operation to invalidate cache for an entire API source.
261325
/// Useful when external API structure changes.
262-
/// </remarks>
326+
/// <summary>
327+
/// Marks all ApiDataItem entities that originate from the specified source URL as stale.
328+
/// </summary>
329+
/// <param name="sourceUrl">The source URL whose items should be marked stale.</param>
330+
/// <param name="cancellationToken">A token to cancel the operation.</param>
331+
/// <returns>The number of items that were marked as stale.</returns>
263332
Task<int> MarkSourceAsStaleAsync(string sourceUrl, CancellationToken cancellationToken = default);
264333

265334
/// <summary>
@@ -270,7 +339,10 @@ public interface IApiDataItemRepository
270339
/// <remarks>
271340
/// Provides insights for cache performance and data freshness monitoring.
272341
/// Returns counts by status, average age, etc.
273-
/// </remarks>
342+
/// <summary>
343+
/// Retrieves aggregated statistics for ApiDataItem entities such as counts by status, age metrics, and other summary telemetry.
344+
/// </summary>
345+
/// <returns>An <see cref="ApiDataStatisticsDto"/> containing counts, averages, and other summary metrics for ApiDataItem entities.</returns>
274346
Task<ApiDataStatisticsDto> GetStatisticsAsync(CancellationToken cancellationToken = default);
275347

276348
/// <summary>
@@ -281,7 +353,10 @@ public interface IApiDataItemRepository
281353
/// <remarks>
282354
/// Commits the unit of work transaction.
283355
/// Should be called after Add/Update/Delete operations.
284-
/// </remarks>
356+
/// <summary>
357+
/// Persists all pending changes tracked by the repository to the underlying data store.
358+
/// </summary>
359+
/// <returns>The number of state entries written to the underlying data store.</returns>
285360
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
286361
}
287-
}
362+
}

0 commit comments

Comments
 (0)