using System.Collections.Immutable; using System.Runtime.CompilerServices; using Connected.Middleware; using Connected.Security.Authorization; using Connected.ServiceModel; using Connected.ServiceModel.Transactions; using Connected.Services.Middleware; using Connected.Validation; namespace Connected.Services; public abstract class Service : IService, IDisposable { protected Service(IContext context) { Context = context; if (Context.GetService() is not IMiddlewareService middleware) throw new NullReferenceException(nameof(IMiddlewareService)); Middleware = middleware; } protected IContext Context { get; } private IMiddlewareService Middleware { get; } protected bool IsDisposed { get; private set; } protected async Task Invoke(IFunction function, TArgs args, [CallerMemberName] string? method = null) where TArgs : IDto { var ctx = await Prepare(function, args, method); var result = await function.Invoke(args); var middleware = await Middleware.Query>(ctx); if (!middleware.IsEmpty) { foreach (var m in middleware) result = await m.Invoke(args, result); } return await Authorize(ctx, args, result); } protected async Task Invoke(INullableFunction function, TArgs args, [CallerMemberName] string? method = null) where TArgs : IDto { var ctx = await Prepare(function, args, method); var result = await function.Invoke(args); var middleware = await Middleware.Query>(ctx); if (!middleware.IsEmpty) { foreach (var m in middleware) result = await m.Invoke(args, result); } return await Authorize(ctx, args, result); } protected async Task Invoke(IAction action, TArgs args, [CallerMemberName] string? method = null) where TArgs : IDto { var ctx = await Prepare(action, args, method); await action.Invoke(args); var middleware = await Middleware.Query>(ctx); if (!middleware.IsEmpty) { foreach (var m in middleware) await m.Invoke(args); } } private async Task Prepare(IServiceOperation operation, TArgs args, [CallerMemberName] string? method = null) where TArgs : IDto { if (operation is ITransactionClient client && Context.GetService() is ITransactionContext transaction) transaction.Register(client); var ctx = new CallerContext(this, method); await ProvideArgumentValues(args); Validate(ctx, args); await Authorize(ctx, args); return ctx; } public TOperation GetOperation([CallerMemberName] string? method = null) { return Context.GetService(); } private void Validate(ICallerContext context, TArgs args) where TArgs : IDto { if (Context.GetService() is not IValidationContext validationContext) return; validationContext.Validate(context, args); } private async Task Authorize(ICallerContext context, TArgs args) where TArgs : IDto { if (Context.GetService() is not IAuthorizationContext authorization) return; await authorization.Authorize(context, args); } private async Task Authorize(ICallerContext context, TArgs args, TResult result) where TArgs : IDto { /* * TODO: call data protection middleware and authorize each entity in the result. * Will need to implement iterator which resolves all entities in the result set. */ await Task.CompletedTask; return result; } private async Task ProvideArgumentValues(TArgs args) where TArgs : IDto { if (await Middleware.Query>() is not ImmutableList> middleware || middleware.IsEmpty) return; foreach (var m in middleware) await m.Invoke(args); } private void Dispose(bool disposing) { if (!IsDisposed) { if (disposing) OnDisposing(); IsDisposed = true; } } protected virtual void OnDisposing() { } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } }