| | | 1 | | using Microsoft.Extensions.DependencyInjection; |
| | | 2 | | using TeleFlow.Framework.Application; |
| | | 3 | | using TeleFlow.Framework.Dispatching; |
| | | 4 | | using TeleFlow.Framework.Middleware; |
| | | 5 | | |
| | | 6 | | namespace TeleFlow.Framework.Updates; |
| | | 7 | | |
| | | 8 | | internal sealed class DefaultUpdateProcessor : IUpdateProcessor |
| | | 9 | | { |
| | | 10 | | private readonly IServiceScopeFactory _scopeFactory; |
| | | 11 | | private readonly IUpdateDispatcher _dispatcher; |
| | | 12 | | private readonly IReadOnlyList<UpdateMiddlewareRegistration> _middleware; |
| | 91 | 13 | | private readonly object _validationLock = new(); |
| | | 14 | | private bool _runtimeValidated; |
| | | 15 | | |
| | | 16 | | public DefaultUpdateProcessor( |
| | | 17 | | IServiceScopeFactory scopeFactory, |
| | | 18 | | IUpdateDispatcher dispatcher, |
| | | 19 | | IEnumerable<UpdateMiddlewareRegistration> middleware) |
| | | 20 | | { |
| | 91 | 21 | | _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory)); |
| | 91 | 22 | | _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); |
| | 91 | 23 | | ArgumentNullException.ThrowIfNull(middleware); |
| | | 24 | | |
| | 91 | 25 | | _middleware = middleware.ToArray(); |
| | 91 | 26 | | } |
| | | 27 | | |
| | | 28 | | public async Task ProcessAsync(IUpdatePayload payload, CancellationToken cancellationToken = default) |
| | | 29 | | { |
| | 91 | 30 | | ArgumentNullException.ThrowIfNull(payload); |
| | | 31 | | |
| | 91 | 32 | | var scope = _scopeFactory.CreateAsyncScope(); |
| | 91 | 33 | | await using (scope.ConfigureAwait(false)) |
| | | 34 | | { |
| | 91 | 35 | | EnsureRuntimeValidated(scope.ServiceProvider); |
| | | 36 | | |
| | 90 | 37 | | var context = new UpdateContext(scope.ServiceProvider, payload, cancellationToken); |
| | 90 | 38 | | var currentUpdate = scope.ServiceProvider.GetService<IUpdateContextAccessorInitializer>(); |
| | 90 | 39 | | var pipeline = BuildPipeline(scope.ServiceProvider, _dispatcher, _middleware); |
| | | 40 | | |
| | 90 | 41 | | currentUpdate?.Initialize(context); |
| | | 42 | | |
| | | 43 | | try |
| | | 44 | | { |
| | 90 | 45 | | await pipeline(context).ConfigureAwait(false); |
| | 84 | 46 | | } |
| | | 47 | | finally |
| | | 48 | | { |
| | 90 | 49 | | currentUpdate?.Clear(context); |
| | | 50 | | } |
| | 84 | 51 | | } |
| | 84 | 52 | | } |
| | | 53 | | |
| | | 54 | | private void EnsureRuntimeValidated(IServiceProvider services) |
| | | 55 | | { |
| | 91 | 56 | | if (_runtimeValidated) |
| | | 57 | | { |
| | 16 | 58 | | return; |
| | | 59 | | } |
| | | 60 | | |
| | 75 | 61 | | lock (_validationLock) |
| | | 62 | | { |
| | 75 | 63 | | if (_runtimeValidated) |
| | | 64 | | { |
| | 0 | 65 | | return; |
| | | 66 | | } |
| | | 67 | | |
| | 75 | 68 | | TeleFlowRuntimeValidatorRunner.Validate(services); |
| | 74 | 69 | | _runtimeValidated = true; |
| | 74 | 70 | | } |
| | 74 | 71 | | } |
| | | 72 | | |
| | | 73 | | private static UpdateDelegate BuildPipeline( |
| | | 74 | | IServiceProvider services, |
| | | 75 | | IUpdateDispatcher dispatcher, |
| | | 76 | | IReadOnlyList<UpdateMiddlewareRegistration> middleware) |
| | | 77 | | { |
| | 177 | 78 | | UpdateDelegate pipeline = context => dispatcher.DispatchAsync(context, context.CancellationToken); |
| | | 79 | | |
| | 300 | 80 | | for (var index = middleware.Count - 1; index >= 0; index--) |
| | | 81 | | { |
| | 60 | 82 | | var current = middleware[index].Resolve(services); |
| | 60 | 83 | | var next = pipeline; |
| | 120 | 84 | | pipeline = context => current.InvokeAsync(context, next); |
| | | 85 | | } |
| | | 86 | | |
| | 90 | 87 | | return pipeline; |
| | | 88 | | } |
| | | 89 | | } |