Skip to content

Commit a02dda8

Browse files
author
jarvis
committed
refactor: reduce cyclomatic complexity of 12 high-complexity methods
Extracted helper methods to reduce complexity in methods with CC 10-15: Method | Before | After ------------------------------------------------|--------|------ AddHeroRateLimiting | 15 | 3 GetQuery<T> (SpecificationEvaluator) | 13 | 1 ExecuteAsync (AuditBackgroundWorker) | 13 | 5 SendAsync (SendGridMailService) | 12 | 1 PublishSingleAsync (InMemoryEventBus) | 11 | 3 PublishAsync (ChannelAuditPublisher) | 11 | 2 Handle (GetAuditSummaryQueryHandler) | 11 | 1 Handle (GetExceptionAuditsQueryHandler) | 11 | 1 Handle (UpdateGroupCommandHandler) | 11 | 1 UpdatePermissionsAsync (RoleService) | 11 | 2 StartAsync (TenantAutoProvisioningHostedService)| 11 | 3 ApplySortingOverride (Specification<T>) | 11 | 3 Refactoring patterns used: - Extract Method: Split large methods into smaller focused helpers - Single Responsibility: Each helper does one thing - Reduced nesting: Flattened control flow structures All changes pass build verification with 0 errors and 0 warnings.
1 parent de81c6f commit a02dda8

12 files changed

Lines changed: 583 additions & 353 deletions

File tree

src/BuildingBlocks/Eventing/InMemory/InMemoryEventBus.cs

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -42,58 +42,83 @@ private async Task PublishSingleAsync(IIntegrationEvent @event, CancellationToke
4242
using var scope = _serviceProvider.CreateScope();
4343
var provider = scope.ServiceProvider;
4444

45-
var handlerInterfaceType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
46-
var handlers = provider.GetServices(handlerInterfaceType).ToArray();
47-
45+
var handlers = ResolveHandlers(provider, eventType);
4846
if (handlers.Length == 0)
4947
{
5048
_logger.LogDebug("No handlers registered for integration event type {EventType}", eventType.FullName);
5149
return;
5250
}
5351

5452
var inbox = provider.GetService<IInboxStore>();
53+
var handlerInterfaceType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
5554

5655
foreach (var handler in handlers)
5756
{
58-
if (handler is null)
59-
{
60-
continue;
61-
}
57+
await InvokeHandlerAsync(handler, handlerInterfaceType, eventType, @event, inbox, ct);
58+
}
59+
}
6260

63-
var handlerName = handler.GetType().FullName ?? handler.GetType().Name;
61+
private static object[] ResolveHandlers(IServiceProvider provider, Type eventType)
62+
{
63+
var handlerInterfaceType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
64+
return provider.GetServices(handlerInterfaceType).Where(h => h is not null).ToArray()!;
65+
}
6466

65-
if (inbox != null)
66-
{
67-
if (await inbox.HasProcessedAsync(@event.Id, handlerName, ct).ConfigureAwait(false))
68-
{
69-
_logger.LogDebug("Skipping already processed integration event {EventId} for handler {Handler}", @event.Id, handlerName);
70-
continue;
71-
}
72-
}
67+
private async Task InvokeHandlerAsync(
68+
object handler,
69+
Type handlerInterfaceType,
70+
Type eventType,
71+
IIntegrationEvent @event,
72+
IInboxStore? inbox,
73+
CancellationToken ct)
74+
{
75+
var handlerName = handler.GetType().FullName ?? handler.GetType().Name;
7376

74-
var method = handlerInterfaceType.GetMethod(nameof(IIntegrationEventHandler<IIntegrationEvent>.HandleAsync));
75-
if (method == null)
76-
{
77-
_logger.LogWarning("Handler {Handler} does not implement HandleAsync correctly for {EventType}", handlerName, eventType.FullName);
78-
continue;
79-
}
77+
if (await ShouldSkipProcessedEventAsync(inbox, @event.Id, handlerName, ct))
78+
{
79+
_logger.LogDebug("Skipping already processed integration event {EventId} for handler {Handler}", @event.Id, handlerName);
80+
return;
81+
}
8082

81-
try
82-
{
83-
var task = (Task)method.Invoke(handler, new object[] { @event, ct })!;
84-
await task.ConfigureAwait(false);
85-
86-
if (inbox != null)
87-
{
88-
await inbox.MarkProcessedAsync(@event.Id, handlerName, @event.TenantId, eventType.AssemblyQualifiedName ?? eventType.FullName!, ct)
89-
.ConfigureAwait(false);
90-
}
91-
}
92-
catch (Exception ex)
83+
var method = handlerInterfaceType.GetMethod(nameof(IIntegrationEventHandler<IIntegrationEvent>.HandleAsync));
84+
if (method == null)
85+
{
86+
_logger.LogWarning("Handler {Handler} does not implement HandleAsync correctly for {EventType}", handlerName, eventType.FullName);
87+
return;
88+
}
89+
90+
await ExecuteHandlerAsync(handler, method, @event, eventType, handlerName, inbox, ct);
91+
}
92+
93+
private static async Task<bool> ShouldSkipProcessedEventAsync(IInboxStore? inbox, Guid eventId, string handlerName, CancellationToken ct)
94+
{
95+
return inbox != null && await inbox.HasProcessedAsync(eventId, handlerName, ct).ConfigureAwait(false);
96+
}
97+
98+
private async Task ExecuteHandlerAsync(
99+
object handler,
100+
MethodInfo method,
101+
IIntegrationEvent @event,
102+
Type eventType,
103+
string handlerName,
104+
IInboxStore? inbox,
105+
CancellationToken ct)
106+
{
107+
try
108+
{
109+
var task = (Task)method.Invoke(handler, new object[] { @event, ct })!;
110+
await task.ConfigureAwait(false);
111+
112+
if (inbox != null)
93113
{
94-
_logger.LogError(ex, "Error while handling integration event {EventId} with handler {Handler}", @event.Id, handlerName);
95-
throw;
114+
await inbox.MarkProcessedAsync(@event.Id, handlerName, @event.TenantId, eventType.AssemblyQualifiedName ?? eventType.FullName!, ct)
115+
.ConfigureAwait(false);
96116
}
97117
}
118+
catch (Exception ex)
119+
{
120+
_logger.LogError(ex, "Error while handling integration event {EventId} with handler {Handler}", @event.Id, handlerName);
121+
throw;
122+
}
98123
}
99124
}

src/BuildingBlocks/Mailing/Services/SendGridMailService.cs

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,61 @@ public SendGridMailService(IOptions<MailOptions> settings)
1919
public async Task SendAsync(MailRequest request, CancellationToken ct)
2020
{
2121
ArgumentNullException.ThrowIfNull(request);
22-
if (_settings.SendGrid?.ApiKey is null)
23-
throw new InvalidOperationException("SendGrid ApiKey is not configured.");
22+
ValidateConfiguration();
2423

25-
var client = new SendGridClient(_settings.SendGrid.ApiKey);
26-
var from = new EmailAddress(request.From ?? _settings.SendGrid.From ?? _settings.From, request.DisplayName ?? _settings.SendGrid.DisplayName ?? _settings.DisplayName);
24+
var client = new SendGridClient(_settings.SendGrid!.ApiKey!);
25+
var from = CreateFromAddress(request);
2726
var msg = MailHelper.CreateSingleEmail(
2827
from,
2928
new EmailAddress(request.To[0]),
3029
request.Subject,
3130
request.Body,
3231
request.Body);
3332

33+
ConfigureRecipients(msg, request);
34+
AddAttachments(msg, request);
35+
36+
await client.SendEmailAsync(msg, ct);
37+
}
38+
39+
private void ValidateConfiguration()
40+
{
41+
if (_settings.SendGrid?.ApiKey is null)
42+
{
43+
throw new InvalidOperationException("SendGrid ApiKey is not configured.");
44+
}
45+
}
46+
47+
private EmailAddress CreateFromAddress(MailRequest request)
48+
{
49+
var email = request.From ?? _settings.SendGrid?.From ?? _settings.From;
50+
var displayName = request.DisplayName ?? _settings.SendGrid?.DisplayName ?? _settings.DisplayName;
51+
return new EmailAddress(email, displayName);
52+
}
53+
54+
private static void ConfigureRecipients(SendGridMessage msg, MailRequest request)
55+
{
3456
if (request.Cc.Count > 0)
57+
{
3558
msg.AddCcs(request.Cc.Select(cc => new EmailAddress(cc)).ToList());
59+
}
60+
3661
if (request.Bcc.Count > 0)
62+
{
3763
msg.AddBccs(request.Bcc.Select(bcc => new EmailAddress(bcc)).ToList());
64+
}
65+
3866
if (request.ReplyTo != null)
67+
{
3968
msg.ReplyTo = new EmailAddress(request.ReplyTo, request.ReplyToName);
69+
}
70+
}
4071

41-
// Attachments
42-
if (request.AttachmentData.Count > 0)
72+
private static void AddAttachments(SendGridMessage msg, MailRequest request)
73+
{
74+
foreach (var att in request.AttachmentData)
4375
{
44-
foreach (var att in request.AttachmentData)
45-
{
46-
msg.AddAttachment(att.Key, Convert.ToBase64String(att.Value));
47-
}
76+
msg.AddAttachment(att.Key, Convert.ToBase64String(att.Value));
4877
}
49-
50-
await client.SendEmailAsync(msg, ct);
5178
}
5279
}

src/BuildingBlocks/Persistence/Specifications/Specification.cs

Lines changed: 41 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -153,75 +153,68 @@ protected void ApplySortingOverride(
153153
ArgumentNullException.ThrowIfNull(applyDefaultOrdering);
154154
ArgumentNullException.ThrowIfNull(sortMappings);
155155

156+
ClearOrderExpressions();
157+
156158
if (string.IsNullOrWhiteSpace(sortExpression))
157159
{
158-
ClearOrderExpressions();
159160
applyDefaultOrdering();
160161
return;
161162
}
162163

163-
ClearOrderExpressions();
164+
var clauses = ParseSortClauses(sortExpression);
165+
bool anyApplied = ApplySortClauses(clauses, sortMappings);
164166

165-
string[] clauses = sortExpression.Split(
166-
',',
167-
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
167+
if (!anyApplied)
168+
{
169+
applyDefaultOrdering();
170+
}
171+
}
168172

173+
private static IEnumerable<string> ParseSortClauses(string sortExpression)
174+
{
175+
return sortExpression.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
176+
.Where(clause => !string.IsNullOrWhiteSpace(clause));
177+
}
178+
179+
private bool ApplySortClauses(IEnumerable<string> clauses, IReadOnlyDictionary<string, Expression<Func<T, object>>> sortMappings)
180+
{
169181
bool anyApplied = false;
170182

171183
foreach (string rawClause in clauses)
172184
{
173-
if (string.IsNullOrWhiteSpace(rawClause))
185+
var (key, descending) = ParseSortClause(rawClause);
186+
187+
if (string.IsNullOrWhiteSpace(key) || !sortMappings.TryGetValue(key, out var selector))
174188
{
175189
continue;
176190
}
177191

178-
string clause = rawClause.Trim();
179-
bool descending = clause[0] == '-';
180-
if (clause[0] is '-' or '+')
181-
{
182-
clause = clause[1..];
183-
}
192+
ApplySortOrder(selector, descending, anyApplied);
193+
anyApplied = true;
194+
}
184195

185-
if (string.IsNullOrWhiteSpace(clause))
186-
{
187-
continue;
188-
}
196+
return anyApplied;
197+
}
189198

190-
if (!sortMappings.TryGetValue(clause, out Expression<Func<T, object>>? selector))
191-
{
192-
// Unknown sort key; skip to keep sorting safe.
193-
continue;
194-
}
199+
private static (string key, bool descending) ParseSortClause(string clause)
200+
{
201+
clause = clause.Trim();
202+
bool descending = clause[0] == '-';
203+
string key = clause[0] is '-' or '+' ? clause[1..] : clause;
204+
return (key, descending);
205+
}
195206

196-
if (!anyApplied)
197-
{
198-
if (descending)
199-
{
200-
OrderByDescending(selector);
201-
}
202-
else
203-
{
204-
OrderBy(selector);
205-
}
206-
207-
anyApplied = true;
208-
}
209-
else
210-
{
211-
if (descending)
212-
{
213-
ThenByDescending(selector);
214-
}
215-
else
216-
{
217-
ThenBy(selector);
218-
}
219-
}
207+
private void ApplySortOrder(Expression<Func<T, object>> selector, bool descending, bool isSecondary)
208+
{
209+
if (isSecondary)
210+
{
211+
if (descending) ThenByDescending(selector);
212+
else ThenBy(selector);
220213
}
221-
222-
if (!anyApplied)
214+
else
223215
{
224-
applyDefaultOrdering();
216+
if (descending) OrderByDescending(selector);
217+
else OrderBy(selector);
225218
}
226219
}
227220

0 commit comments

Comments
 (0)