-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathServiceCollectionExtensions.cs
More file actions
120 lines (102 loc) · 5.68 KB
/
Copy pathServiceCollectionExtensions.cs
File metadata and controls
120 lines (102 loc) · 5.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using System.Net.Http.Headers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace GroundControl.Link;
/// <summary>
/// Extension methods for <see cref="IServiceCollection"/> to register GroundControl background services.
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Registers GroundControl background services: connection strategy, health check, and metrics.
/// Requires configuration to have been set up via <see cref="ConfigurationBuilderExtensions.AddGroundControl"/>.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="configuration">The built configuration root containing the GroundControl provider.</param>
/// <param name="configureHttpClient">An optional delegate to customize the GroundControl <see cref="HttpClient"/> builder.</param>
/// <param name="configureOptions">
/// An optional delegate to further configure <see cref="GroundControlOptions"/> after loading from configuration.
/// Note that options are already applied when the configuration provider is created, so this is only for additional settings that don't affect the core provider services.
/// </param>
/// <returns>The service collection for chaining.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown when no <see cref="GroundControlConfigurationProvider"/> is found in the configuration.
/// Call <see cref="ConfigurationBuilderExtensions.AddGroundControl"/> first.
/// </exception>
public static IServiceCollection AddGroundControl(
this IServiceCollection services,
IConfigurationRoot configuration,
Action<IHttpClientBuilder>? configureHttpClient = null,
Action<GroundControlOptions>? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(configuration);
var provider = FindProvider(configuration);
services.AddSingleton(provider.Store);
services.AddSingleton(provider.Cache);
var options = provider.Store.Options;
configureOptions?.Invoke(options);
services.AddSingleton(Options.Create(options));
services.AddMetrics();
services.AddSingleton<GroundControlMetrics>();
services.AddHealthChecks().AddCheck<LinkHealthCheck>("GroundControl", tags: options.HealthCheckTags);
if (options.ConnectionMode == ConnectionMode.StartupOnly)
{
return services;
}
var httpBuilder = services.AddHttpClient<IGroundControlApiClient, GroundControlApiClient>(httpClient =>
{
httpClient.BaseAddress = options.ServerUrl;
httpClient.DefaultRequestHeaders.Add(HeaderNames.ApiVersion, options.ApiVersion);
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(HeaderNames.ApiKey, $"{options.ClientId}:{options.ClientSecret}");
if (options.Scopes.Count > 0)
{
var scopeValue = string.Join(",", options.Scopes.Select(s => $"{Uri.EscapeDataString(s.Key)}:{Uri.EscapeDataString(s.Value)}"));
httpClient.DefaultRequestHeaders.Add(HeaderNames.GroundControlScopes, scopeValue);
}
})
.UseSocketsHttpHandler((handler, _) => handler.PooledConnectionLifetime = TimeSpan.FromMinutes(2))
.SetHandlerLifetime(Timeout.InfiniteTimeSpan);
configureHttpClient?.Invoke(httpBuilder);
services.AddSingleton<IGroundControlSseClient>(sp =>
{
var groundControlOptions = sp.GetRequiredService<IOptions<GroundControlOptions>>();
if (groundControlOptions.Value.ConnectionMode == ConnectionMode.Polling)
{
return NoOpGroundControlSseClient.Instance;
}
var apiClient = sp.GetRequiredService<IGroundControlApiClient>();
var logger = sp.GetRequiredService<ILoggerFactory>().CreateLogger<GroundControlSseClient>();
return new GroundControlSseClient(apiClient, groundControlOptions, logger);
});
// Connection Strategy
services.AddSingleton<PollingConnectionStrategy>();
services.AddSingleton<SseConnectionStrategy>();
services.AddSingleton<SseWithPollingFallbackStrategy>();
services.AddSingleton<IConnectionStrategy>(sp => options.ConnectionMode switch
{
ConnectionMode.Polling => sp.GetRequiredService<PollingConnectionStrategy>(),
ConnectionMode.Sse => sp.GetRequiredService<SseConnectionStrategy>(),
ConnectionMode.SseWithPollingFallback => sp.GetRequiredService<SseWithPollingFallbackStrategy>(),
ConnectionMode.StartupOnly => throw new InvalidOperationException($"Connection strategy should not be registered for {nameof(ConnectionMode.StartupOnly)} mode."),
_ => throw new InvalidOperationException($"Unsupported connection mode: {options.ConnectionMode}")
});
// Background service
services.AddHostedService<LinkBackgroundService>();
return services;
}
private static GroundControlConfigurationProvider FindProvider(IConfigurationRoot configuration)
{
foreach (var provider in configuration.Providers)
{
if (provider is GroundControlConfigurationProvider gcProvider)
{
return gcProvider;
}
}
throw new InvalidOperationException(
"No GroundControlConfigurationProvider found in configuration. " +
"Call IConfigurationBuilder.AddGroundControl() before calling IServiceCollection.AddGroundControl().");
}
}