Skip to content

Commit 0adebdd

Browse files
Update README.md: Revamp contact and connection info
Removed the "Support & Contact" section and replaced it with a new "Stay Connected" section. This update includes links to watch the repository, follow the maintainer on GitHub, connect on LinkedIn, and access the changelog. The previous closing statement about collaboration was also removed, along with the "Built with Love & Passion for Clean Code" section. Merge branch 'Dev' of https://github.com/dariemcarlosdev/SecureCleanApiWaf into Dev
2 parents 2780fe3 + 0a449e3 commit 0adebdd

File tree

59 files changed

+1605
-367
lines changed

Some content is hidden

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

59 files changed

+1605
-367
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)