Skip to content

Commit a051228

Browse files
committed
tests and helpers
1 parent 12793d9 commit a051228

File tree

14 files changed

+882
-137
lines changed

14 files changed

+882
-137
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace ManagedCode.Communication.AspNetCore.Configuration;
2+
3+
/// <summary>
4+
/// Configuration options for Communication library in ASP.NET Core applications
5+
/// </summary>
6+
public class CommunicationOptions
7+
{
8+
/// <summary>
9+
/// Gets or sets whether to show detailed error information in responses
10+
/// </summary>
11+
public bool ShowErrorDetails { get; set; } = false;
12+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.Extensions.Hosting;
6+
using Microsoft.Extensions.Logging;
7+
using ManagedCode.Communication.Logging;
8+
using ManagedCode.Communication.AspNetCore.Configuration;
9+
using Microsoft.AspNetCore.Mvc;
10+
11+
namespace ManagedCode.Communication.AspNetCore.Extensions;
12+
13+
/// <summary>
14+
/// Extension methods for configuring Communication library in ASP.NET Core applications
15+
/// </summary>
16+
public static class CommunicationServiceCollectionExtensions
17+
{
18+
/// <summary>
19+
/// Configures Communication library to use the service provider for logging in ASP.NET Core applications.
20+
/// Uses a hosted service to configure logging after DI container is fully built.
21+
/// </summary>
22+
public static IServiceCollection AddCommunicationAspNetCore(this IServiceCollection services)
23+
{
24+
services.AddHostedService<CommunicationLoggerConfigurationService>();
25+
return services;
26+
}
27+
28+
/// <summary>
29+
/// Configures Communication library with a specific logger factory
30+
/// </summary>
31+
public static IServiceCollection AddCommunicationAspNetCore(this IServiceCollection services, ILoggerFactory loggerFactory)
32+
{
33+
CommunicationLogger.Configure(loggerFactory);
34+
return services;
35+
}
36+
37+
/// <summary>
38+
/// Adds Communication filters to MVC controllers.
39+
/// This is a legacy method for backward compatibility.
40+
/// </summary>
41+
public static IServiceCollection AddCommunicationFilters(this IServiceCollection services)
42+
{
43+
services.Configure<MvcOptions>(options =>
44+
{
45+
options.AddCommunicationFilters();
46+
});
47+
return services;
48+
}
49+
50+
/// <summary>
51+
/// Configures Communication library for ASP.NET Core with options.
52+
/// This is a legacy method for backward compatibility.
53+
/// </summary>
54+
public static IServiceCollection AddCommunication(this IServiceCollection services, Action<CommunicationOptions>? configure = null)
55+
{
56+
var options = new CommunicationOptions();
57+
configure?.Invoke(options);
58+
59+
services.AddCommunicationAspNetCore();
60+
services.AddCommunicationFilters();
61+
62+
return services;
63+
}
64+
}
65+
66+
/// <summary>
67+
/// Hosted service that configures the static logger after DI container is built
68+
/// </summary>
69+
internal class CommunicationLoggerConfigurationService(IServiceProvider serviceProvider) : IHostedService
70+
{
71+
public Task StartAsync(CancellationToken cancellationToken)
72+
{
73+
// Configure the static logger with the fully built service provider
74+
CommunicationLogger.Configure(serviceProvider);
75+
return Task.CompletedTask;
76+
}
77+
78+
public Task StopAsync(CancellationToken cancellationToken)
79+
{
80+
return Task.CompletedTask;
81+
}
82+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using ManagedCode.Communication.AspNetCore.Filters;
2+
using Microsoft.AspNetCore.Mvc;
3+
4+
namespace ManagedCode.Communication.AspNetCore.Extensions;
5+
6+
/// <summary>
7+
/// Extension methods for configuring MVC options with Communication filters
8+
/// </summary>
9+
public static class MvcOptionsExtensions
10+
{
11+
/// <summary>
12+
/// Adds Communication filters to MVC options in the correct order
13+
/// </summary>
14+
public static void AddCommunicationFilters(this MvcOptions options)
15+
{
16+
// Add filters in the correct order for proper functionality
17+
options.Filters.Add<CommunicationModelValidationFilter>();
18+
options.Filters.Add<CommunicationExceptionFilter>();
19+
options.Filters.Add<ResultToActionResultFilter>();
20+
}
21+
}

ManagedCode.Communication.AspNetCore/Extensions/ServiceCollectionExtensions.cs

Lines changed: 0 additions & 72 deletions
This file was deleted.

ManagedCode.Communication.AspNetCore/ManagedCode.Communication.AspNetCore.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
<ItemGroup>
1818
<ProjectReference Include="..\ManagedCode.Communication\ManagedCode.Communication.csproj"/>
1919
</ItemGroup>
20-
20+
2121
<ItemGroup>
2222
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.8" />
23+
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.8"/>
2324
</ItemGroup>
2425

2526
</Project>

ManagedCode.Communication.AspNetCore/SignalR/Extensions/HubOptionsExtensions.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ namespace ManagedCode.Communication.AspNetCore.Extensions;
77

88
public static class HubOptionsExtensions
99
{
10-
public static void AddCommunicationHubFilter(this HubOptions result)
10+
public static void AddCommunicationHubFilter(this HubOptions options)
1111
{
12-
result.AddFilter<CommunicationHubExceptionFilter>();
12+
options.AddFilter<CommunicationHubExceptionFilter>();
13+
}
14+
15+
public static void AddCommunicationFilters(this HubOptions options)
16+
{
17+
options.AddCommunicationHubFilter();
1318
}
1419
}

ManagedCode.Communication.Orleans/Grains/CommandIdempotencyGrain.cs

Lines changed: 40 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,84 +10,77 @@ namespace ManagedCode.Communication.Orleans.Grains;
1010
/// Orleans grain implementation for command idempotency.
1111
/// Stores command execution state and results in grain state.
1212
/// </summary>
13-
public class CommandIdempotencyGrain : Grain, ICommandIdempotencyGrain
13+
public class CommandIdempotencyGrain([PersistentState("commandState", "commandStore")] IPersistentState<CommandState> state)
14+
: Grain, ICommandIdempotencyGrain
1415
{
15-
private readonly IPersistentState<CommandState> _state;
16-
17-
public CommandIdempotencyGrain(
18-
[PersistentState("commandState", "commandStore")] IPersistentState<CommandState> state)
19-
{
20-
_state = state;
21-
}
22-
2316
public Task<CommandExecutionStatus> GetStatusAsync()
2417
{
2518
// Check if expired
26-
if (_state.State.ExpiresAt.HasValue && DateTimeOffset.UtcNow > _state.State.ExpiresAt.Value)
19+
if (state.State.ExpiresAt.HasValue && DateTimeOffset.UtcNow > state.State.ExpiresAt.Value)
2720
{
2821
return Task.FromResult(CommandExecutionStatus.NotFound);
2922
}
3023

31-
return Task.FromResult(_state.State.Status);
24+
return Task.FromResult(state.State.Status);
3225
}
3326

3427
public async Task<bool> TryStartProcessingAsync()
3528
{
3629
// Check if already processing or completed
37-
if (_state.State.Status != CommandExecutionStatus.NotFound)
30+
if (state.State.Status != CommandExecutionStatus.NotFound)
3831
{
3932
return false;
4033
}
4134

42-
_state.State.Status = CommandExecutionStatus.Processing;
43-
_state.State.StartedAt = DateTimeOffset.UtcNow;
44-
_state.State.ExpiresAt = DateTimeOffset.UtcNow.AddHours(1); // Default 1 hour expiration
45-
46-
await _state.WriteStateAsync();
35+
state.State.Status = CommandExecutionStatus.Processing;
36+
state.State.StartedAt = DateTimeOffset.UtcNow;
37+
state.State.ExpiresAt = DateTimeOffset.UtcNow.AddHours(1); // Default 1 hour expiration
38+
39+
await state.WriteStateAsync();
4740
return true;
4841
}
4942

5043
public async Task MarkCompletedAsync<TResult>(TResult result)
5144
{
52-
_state.State.Status = CommandExecutionStatus.Completed;
53-
_state.State.CompletedAt = DateTimeOffset.UtcNow;
54-
_state.State.Result = result;
55-
_state.State.ExpiresAt = DateTimeOffset.UtcNow.AddHours(1);
56-
57-
await _state.WriteStateAsync();
45+
state.State.Status = CommandExecutionStatus.Completed;
46+
state.State.CompletedAt = DateTimeOffset.UtcNow;
47+
state.State.Result = result;
48+
state.State.ExpiresAt = DateTimeOffset.UtcNow.AddHours(1);
49+
50+
await state.WriteStateAsync();
5851
}
5952

6053
public async Task MarkFailedAsync(string errorMessage)
6154
{
62-
_state.State.Status = CommandExecutionStatus.Failed;
63-
_state.State.FailedAt = DateTimeOffset.UtcNow;
64-
_state.State.ErrorMessage = errorMessage;
65-
_state.State.ExpiresAt = DateTimeOffset.UtcNow.AddMinutes(15); // Shorter TTL for failures
66-
67-
await _state.WriteStateAsync();
55+
state.State.Status = CommandExecutionStatus.Failed;
56+
state.State.FailedAt = DateTimeOffset.UtcNow;
57+
state.State.ErrorMessage = errorMessage;
58+
state.State.ExpiresAt = DateTimeOffset.UtcNow.AddMinutes(15); // Shorter TTL for failures
59+
60+
await state.WriteStateAsync();
6861
}
6962

7063
public Task<(bool success, object? result)> TryGetResultAsync()
7164
{
72-
if (_state.State.Status == CommandExecutionStatus.Completed)
65+
if (state.State.Status == CommandExecutionStatus.Completed)
7366
{
74-
return Task.FromResult((true, _state.State.Result));
67+
return Task.FromResult((true, state.State.Result));
7568
}
7669

7770
return Task.FromResult((false, (object?)null));
7871
}
7972

8073
public async Task ClearAsync()
8174
{
82-
_state.State.Status = CommandExecutionStatus.NotFound;
83-
_state.State.Result = null;
84-
_state.State.ErrorMessage = null;
85-
_state.State.StartedAt = null;
86-
_state.State.CompletedAt = null;
87-
_state.State.FailedAt = null;
88-
_state.State.ExpiresAt = null;
89-
90-
await _state.WriteStateAsync();
75+
state.State.Status = CommandExecutionStatus.NotFound;
76+
state.State.Result = null;
77+
state.State.ErrorMessage = null;
78+
state.State.StartedAt = null;
79+
state.State.CompletedAt = null;
80+
state.State.FailedAt = null;
81+
state.State.ExpiresAt = null;
82+
83+
await state.WriteStateAsync();
9184
}
9285
}
9386

@@ -99,22 +92,22 @@ public class CommandState
9992
{
10093
[Id(0)]
10194
public CommandExecutionStatus Status { get; set; } = CommandExecutionStatus.NotFound;
102-
95+
10396
[Id(1)]
10497
public object? Result { get; set; }
105-
98+
10699
[Id(2)]
107100
public string? ErrorMessage { get; set; }
108-
101+
109102
[Id(3)]
110103
public DateTimeOffset? StartedAt { get; set; }
111-
104+
112105
[Id(4)]
113106
public DateTimeOffset? CompletedAt { get; set; }
114-
107+
115108
[Id(5)]
116109
public DateTimeOffset? FailedAt { get; set; }
117-
110+
118111
[Id(6)]
119112
public DateTimeOffset? ExpiresAt { get; set; }
120-
}
113+
}

0 commit comments

Comments
 (0)