|
|
|
|
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<IMiddlewareService>() 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<TReturnValue> Invoke<TArgs, TReturnValue>(IFunction<TArgs, TReturnValue> 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<IFunctionMiddleware<TArgs, TReturnValue>>(ctx);
|
|
|
|
|
|
|
|
|
|
if (!middleware.IsEmpty)
|
|
|
|
|
{
|
|
|
|
|
foreach (var m in middleware)
|
|
|
|
|
result = await m.Invoke(args, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await Authorize(ctx, args, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected async Task<TReturnValue?> Invoke<TArgs, TReturnValue>(INullableFunction<TArgs, TReturnValue> 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<IFunctionMiddleware<TArgs, TReturnValue>>(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<TArgs>(IAction<TArgs> 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<IActionMiddleware<TArgs>>(ctx);
|
|
|
|
|
|
|
|
|
|
if (!middleware.IsEmpty)
|
|
|
|
|
{
|
|
|
|
|
foreach (var m in middleware)
|
|
|
|
|
await m.Invoke(args);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<ICallerContext> Prepare<TArgs>(IServiceOperation<TArgs> operation, TArgs args, [CallerMemberName] string? method = null)
|
|
|
|
|
where TArgs : IDto
|
|
|
|
|
{
|
|
|
|
|
if (operation is ITransactionClient client && Context.GetService<ITransactionContext>() 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<TOperation>([CallerMemberName] string? method = null)
|
|
|
|
|
{
|
|
|
|
|
return Context.GetService<TOperation>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Validate<TArgs>(ICallerContext context, TArgs args)
|
|
|
|
|
where TArgs : IDto
|
|
|
|
|
{
|
|
|
|
|
if (Context.GetService<IValidationContext>() is not IValidationContext validationContext)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
validationContext.Validate(context, args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task Authorize<TArgs>(ICallerContext context, TArgs args)
|
|
|
|
|
where TArgs : IDto
|
|
|
|
|
{
|
|
|
|
|
if (Context.GetService<IAuthorizationContext>() is not IAuthorizationContext authorization)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
await authorization.Authorize(context, args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<TResult> Authorize<TArgs, TResult>(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>(TArgs args)
|
|
|
|
|
where TArgs : IDto
|
|
|
|
|
{
|
|
|
|
|
if (await Middleware.Query<IArgumentValueProvider<TArgs>>() is not ImmutableList<IArgumentValueProvider<TArgs>> 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);
|
|
|
|
|
}
|
|
|
|
|
}
|