update namespaces
refactor the name of the organisation
This commit is contained in:
parent
88c86513e9
commit
1c81288895
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions.Attributes;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
|
public class CommandNameAttribute : Attribute
|
||||||
|
{
|
||||||
|
public CommandNameAttribute(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions.Attributes;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
|
public class QueryNameAttribute : Attribute
|
||||||
|
{
|
||||||
|
public QueryNameAttribute(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
}
|
||||||
|
|
51
OpenHarbor.CQRS.Abstractions/Discovery/CommandMeta.cs
Normal file
51
OpenHarbor.CQRS.Abstractions/Discovery/CommandMeta.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Attributes;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
|
||||||
|
public sealed class CommandMeta : ICommandMeta
|
||||||
|
{
|
||||||
|
public CommandMeta(Type commandType, Type serviceType, Type commandResultType)
|
||||||
|
{
|
||||||
|
CommandType = commandType;
|
||||||
|
ServiceType = serviceType;
|
||||||
|
CommandResultType = commandResultType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandMeta(Type commandType, Type serviceType)
|
||||||
|
{
|
||||||
|
CommandType = commandType;
|
||||||
|
ServiceType = serviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandNameAttribute NameAttribute => CommandType.GetCustomAttribute<CommandNameAttribute>();
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var name = NameAttribute?.Name ?? CommandType.Name.Replace("Command", string.Empty);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type CommandType { get; }
|
||||||
|
public Type ServiceType { get; }
|
||||||
|
public Type CommandResultType { get; }
|
||||||
|
|
||||||
|
public string LowerCamelCaseName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Name))
|
||||||
|
return Name;
|
||||||
|
|
||||||
|
var name = Name;
|
||||||
|
var firstLetter = Char.ToLowerInvariant(name[0]);
|
||||||
|
var ret = $"{firstLetter}{name.Substring(1)}";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
13
OpenHarbor.CQRS.Abstractions/Discovery/ICommandMeta.cs
Normal file
13
OpenHarbor.CQRS.Abstractions/Discovery/ICommandMeta.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
|
||||||
|
public interface ICommandMeta
|
||||||
|
{
|
||||||
|
string Name { get; }
|
||||||
|
Type CommandType { get; }
|
||||||
|
Type ServiceType { get; }
|
||||||
|
Type CommandResultType { get; }
|
||||||
|
string LowerCamelCaseName { get; }
|
||||||
|
}
|
||||||
|
|
23
OpenHarbor.CQRS.Abstractions/Discovery/IQueryDiscovery.cs
Normal file
23
OpenHarbor.CQRS.Abstractions/Discovery/IQueryDiscovery.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
|
||||||
|
public interface IQueryDiscovery
|
||||||
|
{
|
||||||
|
IQueryMeta FindQuery(string name);
|
||||||
|
IQueryMeta FindQuery(Type queryType);
|
||||||
|
IEnumerable<IQueryMeta> GetQueries();
|
||||||
|
bool QueryExists(string name);
|
||||||
|
bool QueryExists(Type queryType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ICommandDiscovery
|
||||||
|
{
|
||||||
|
bool CommandExists(string name);
|
||||||
|
bool CommandExists(Type commandType);
|
||||||
|
ICommandMeta FindCommand(string name);
|
||||||
|
ICommandMeta FindCommand(Type commandType);
|
||||||
|
IEnumerable<ICommandMeta> GetCommands();
|
||||||
|
}
|
||||||
|
|
13
OpenHarbor.CQRS.Abstractions/Discovery/IQueryMeta.cs
Normal file
13
OpenHarbor.CQRS.Abstractions/Discovery/IQueryMeta.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
|
||||||
|
public interface IQueryMeta
|
||||||
|
{
|
||||||
|
string Name { get; }
|
||||||
|
Type QueryType { get; }
|
||||||
|
Type ServiceType { get; }
|
||||||
|
Type QueryResultType { get; }
|
||||||
|
string Category { get; }
|
||||||
|
string LowerCamelCaseName { get; }
|
||||||
|
}
|
46
OpenHarbor.CQRS.Abstractions/Discovery/QueryMeta.cs
Normal file
46
OpenHarbor.CQRS.Abstractions/Discovery/QueryMeta.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Attributes;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
|
||||||
|
public class QueryMeta : IQueryMeta
|
||||||
|
{
|
||||||
|
public QueryMeta(Type queryType, Type serviceType, Type queryResultType)
|
||||||
|
{
|
||||||
|
QueryType = queryType;
|
||||||
|
ServiceType = serviceType;
|
||||||
|
QueryResultType = queryResultType;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual QueryNameAttribute NameAttribute => QueryType.GetCustomAttribute<QueryNameAttribute>();
|
||||||
|
|
||||||
|
public virtual string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var name = NameAttribute?.Name ?? QueryType.Name.Replace("Query", string.Empty);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Type QueryType { get; }
|
||||||
|
public virtual Type ServiceType { get; }
|
||||||
|
public virtual Type QueryResultType { get; }
|
||||||
|
public virtual string Category => "BasicQuery";
|
||||||
|
|
||||||
|
public string LowerCamelCaseName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Name))
|
||||||
|
return Name;
|
||||||
|
|
||||||
|
var name = Name;
|
||||||
|
var firstLetter = char.ToLowerInvariant(name[0]);
|
||||||
|
var ret = $"{firstLetter}{name[1..]}";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
16
OpenHarbor.CQRS.Abstractions/ICommandHandler.cs
Normal file
16
OpenHarbor.CQRS.Abstractions/ICommandHandler.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions;
|
||||||
|
|
||||||
|
public interface ICommandHandler<in TCommand>
|
||||||
|
where TCommand : class
|
||||||
|
{
|
||||||
|
Task HandleAsync(TCommand command, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ICommandHandler<in TCommand, TCommandResult>
|
||||||
|
where TCommand : class
|
||||||
|
{
|
||||||
|
Task<TCommandResult> HandleAsync(TCommand command, CancellationToken cancellationToken = default);
|
||||||
|
}
|
10
OpenHarbor.CQRS.Abstractions/IQueryHandler.cs
Normal file
10
OpenHarbor.CQRS.Abstractions/IQueryHandler.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions;
|
||||||
|
|
||||||
|
public interface IQueryHandler<in TQuery, TQueryResult>
|
||||||
|
where TQuery : class
|
||||||
|
{
|
||||||
|
Task<TQueryResult> HandleAsync(TQuery query, CancellationToken cancellationToken = default);
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<Copyright>Powered Softwares Inc.</Copyright>
|
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
|
||||||
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;r=g&amp;d=retro</PackageIconUrl>
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
<Company>PoweredSoft</Company>
|
<LangVersion>default</LangVersion>
|
||||||
<Authors>PoweredSoft Team</Authors>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
@ -0,0 +1,8 @@
|
|||||||
|
namespace OpenHarbor.CQRS.Abstractions.Security;
|
||||||
|
|
||||||
|
public enum AuthorizationResult
|
||||||
|
{
|
||||||
|
Unauthorized,
|
||||||
|
Forbidden,
|
||||||
|
Allowed
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions.Security;
|
||||||
|
|
||||||
|
public interface ICommandAuthorizationService
|
||||||
|
{
|
||||||
|
Task<AuthorizationResult> IsAllowedAsync(Type commandType, CancellationToken cancellationToken = default);
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions.Security;
|
||||||
|
|
||||||
|
public interface IQueryAuthorizationService
|
||||||
|
{
|
||||||
|
Task<AuthorizationResult> IsAllowedAsync(Type queryType, CancellationToken cancellationToken = default);
|
||||||
|
}
|
49
OpenHarbor.CQRS.Abstractions/ServiceCollectionExtensions.cs
Normal file
49
OpenHarbor.CQRS.Abstractions/ServiceCollectionExtensions.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Abstractions;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddQuery<TQuery, TQueryResult, TQueryHandler>(this IServiceCollection services)
|
||||||
|
where TQuery : class
|
||||||
|
where TQueryHandler : class, IQueryHandler<TQuery, TQueryResult>
|
||||||
|
{
|
||||||
|
// add handler to DI.
|
||||||
|
services.AddTransient<IQueryHandler<TQuery, TQueryResult>, TQueryHandler>();
|
||||||
|
|
||||||
|
// add for discovery purposes.
|
||||||
|
var queryMeta = new QueryMeta(typeof(TQuery), typeof(IQueryHandler<TQuery, TQueryResult>), typeof(TQueryResult));
|
||||||
|
services.AddSingleton<IQueryMeta>(queryMeta);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddCommand<TCommand, TCommandResult, TCommandHandler>(this IServiceCollection services)
|
||||||
|
where TCommand : class
|
||||||
|
where TCommandHandler : class, ICommandHandler<TCommand, TCommandResult>
|
||||||
|
{
|
||||||
|
// add handler to DI.
|
||||||
|
services.AddTransient<ICommandHandler<TCommand, TCommandResult>, TCommandHandler>();
|
||||||
|
|
||||||
|
// add for discovery purposes.
|
||||||
|
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>), typeof(TCommandResult));
|
||||||
|
services.AddSingleton<ICommandMeta>(commandMeta);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddCommand<TCommand, TCommandHandler>(this IServiceCollection services)
|
||||||
|
where TCommand : class
|
||||||
|
where TCommandHandler : class, ICommandHandler<TCommand>
|
||||||
|
{
|
||||||
|
// add handler to DI.
|
||||||
|
services.AddTransient<ICommandHandler<TCommand>, TCommandHandler>();
|
||||||
|
|
||||||
|
// add for discovery purposes.
|
||||||
|
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>));
|
||||||
|
services.AddSingleton<ICommandMeta>(commandMeta);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Abstractions.Attributes;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
|
public class CommandControllerIgnoreAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Abstractions.Attributes;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
|
public class QueryControllerIgnoreAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
|
||||||
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
38
OpenHarbor.CQRS.AspNetCore/Mvc/CommandController.cs
Normal file
38
OpenHarbor.CQRS.AspNetCore/Mvc/CommandController.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using OpenHarbor.CQRS.Abstractions;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
[Produces("application/json")]
|
||||||
|
[ApiController, Route("api/command/[controller]")]
|
||||||
|
public class CommandController<TCommand> : Controller
|
||||||
|
where TCommand : class
|
||||||
|
{
|
||||||
|
[HttpPost, CommandControllerAuthorization]
|
||||||
|
public async Task<IActionResult> Handle([FromServices] ICommandHandler<TCommand> handler,
|
||||||
|
[FromBody] TCommand command)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
await handler.HandleAsync(command, this.Request.HttpContext.RequestAborted);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Produces("application/json")]
|
||||||
|
[ApiController, Route("api/command/[controller]")]
|
||||||
|
public class CommandController<TCommand, TTCommandResult> : Controller
|
||||||
|
where TCommand : class
|
||||||
|
{
|
||||||
|
[HttpPost, CommandControllerAuthorization]
|
||||||
|
public async Task<ActionResult<TTCommandResult>> Handle([FromServices] ICommandHandler<TCommand, TTCommandResult> handler,
|
||||||
|
[FromBody] TCommand command)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
return Ok(await handler.HandleAsync(command, this.Request.HttpContext.RequestAborted));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Reflection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Security;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class CommandControllerAsyncAuthorizationFilter : IAsyncAuthorizationFilter
|
||||||
|
{
|
||||||
|
private readonly ICommandAuthorizationService _authorizationService;
|
||||||
|
|
||||||
|
public CommandControllerAsyncAuthorizationFilter(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_authorizationService = serviceProvider.GetService<ICommandAuthorizationService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||||
|
{
|
||||||
|
if (_authorizationService == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var action = context.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;
|
||||||
|
if (action == null)
|
||||||
|
throw new Exception("Only Supports controller action descriptor");
|
||||||
|
|
||||||
|
var attribute = action.MethodInfo.GetCustomAttribute<CommandControllerAuthorizationAttribute>();
|
||||||
|
Type commandType;
|
||||||
|
if (attribute?.CommandType != null)
|
||||||
|
commandType = attribute.CommandType;
|
||||||
|
else
|
||||||
|
commandType = action.ControllerTypeInfo.GenericTypeArguments.First();
|
||||||
|
|
||||||
|
var ar = await _authorizationService.IsAllowedAsync(commandType);
|
||||||
|
if (ar == AuthorizationResult.Forbidden)
|
||||||
|
context.Result = new StatusCodeResult(403);
|
||||||
|
else if(ar == AuthorizationResult.Unauthorized)
|
||||||
|
context.Result = new StatusCodeResult(401);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class CommandControllerAuthorizationAttribute : TypeFilterAttribute
|
||||||
|
{
|
||||||
|
public CommandControllerAuthorizationAttribute() : base(typeof(CommandControllerAsyncAuthorizationFilter))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandControllerAuthorizationAttribute(Type commandType) : base(typeof(CommandControllerAsyncAuthorizationFilter))
|
||||||
|
{
|
||||||
|
CommandType = commandType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type CommandType { get; } = null;
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class CommandControllerConvention : IControllerModelConvention
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public CommandControllerConvention(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
this._serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply(ControllerModel controller)
|
||||||
|
{
|
||||||
|
if (!controller.ControllerType.IsGenericType)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!controller.ControllerType.Name.Contains("CommandController"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (controller.ControllerType.Assembly != typeof(CommandControllerConvention).Assembly)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
||||||
|
var commandDiscovery = this._serviceProvider.GetRequiredService<ICommandDiscovery>();
|
||||||
|
var command = commandDiscovery.FindCommand(genericType);
|
||||||
|
controller.ControllerName = command.LowerCamelCaseName;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
using OpenHarbor.CQRS.AspNetCore.Abstractions.Attributes;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class CommandControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
||||||
|
{
|
||||||
|
private readonly ServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public CommandControllerFeatureProvider(ServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
this._serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
|
||||||
|
{
|
||||||
|
var commandDiscovery = this._serviceProvider.GetRequiredService<ICommandDiscovery>();
|
||||||
|
foreach (var f in commandDiscovery.GetCommands())
|
||||||
|
{
|
||||||
|
var ignoreAttribute = f.CommandType.GetCustomAttribute<CommandControllerIgnoreAttribute>();
|
||||||
|
if (ignoreAttribute != null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (f.CommandResultType == null)
|
||||||
|
{
|
||||||
|
var controllerType = typeof(CommandController<>).MakeGenericType(f.CommandType);
|
||||||
|
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||||
|
feature.Controllers.Add(controllerTypeInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var controllerType = typeof(CommandController<,>).MakeGenericType(f.CommandType, f.CommandResultType);
|
||||||
|
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||||
|
feature.Controllers.Add(controllerTypeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class CommandControllerOptions
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
27
OpenHarbor.CQRS.AspNetCore/Mvc/MvcBuilderExensions.cs
Normal file
27
OpenHarbor.CQRS.AspNetCore/Mvc/MvcBuilderExensions.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public static class MvcBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IMvcBuilder AddPoweredSoftQueries(this IMvcBuilder builder, Action<QueryControllerOptions> configuration = null)
|
||||||
|
{
|
||||||
|
var options = new QueryControllerOptions();
|
||||||
|
configuration?.Invoke(options);
|
||||||
|
var services = builder.Services;
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
builder.AddMvcOptions(o => o.Conventions.Add(new QueryControllerConvention(serviceProvider)));
|
||||||
|
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new QueryControllerFeatureProvider(serviceProvider)));
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IMvcBuilder AddPoweredSoftCommands(this IMvcBuilder builder)
|
||||||
|
{
|
||||||
|
var services = builder.Services;
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
builder.AddMvcOptions(o => o.Conventions.Add(new CommandControllerConvention(serviceProvider)));
|
||||||
|
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new CommandControllerFeatureProvider(serviceProvider)));
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
33
OpenHarbor.CQRS.AspNetCore/Mvc/QueryController.cs
Normal file
33
OpenHarbor.CQRS.AspNetCore/Mvc/QueryController.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using OpenHarbor.CQRS.Abstractions;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
[Produces("application/json")]
|
||||||
|
[ApiController, Route("api/query/[controller]")]
|
||||||
|
public class QueryController<TQuery, TQueryResult> : Controller
|
||||||
|
where TQuery : class
|
||||||
|
{
|
||||||
|
[HttpPost, QueryControllerAuthorization]
|
||||||
|
public async Task<ActionResult<TQueryResult>> Handle([FromServices] IQueryHandler<TQuery, TQueryResult> handler,
|
||||||
|
[FromBody] TQuery query)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
|
||||||
|
return Ok(await handler.HandleAsync(query, this.Request.HttpContext.RequestAborted));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet, QueryControllerAuthorization]
|
||||||
|
public async Task<ActionResult<TQueryResult>> HandleGet([FromServices] IQueryHandler<TQuery, TQueryResult> handler,
|
||||||
|
[FromQuery] TQuery query)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
|
||||||
|
return Ok(await handler.HandleAsync(query, this.Request.HttpContext.RequestAborted));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Reflection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Security;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class QueryControllerAsyncAuthorizationFilter : IAsyncAuthorizationFilter
|
||||||
|
{
|
||||||
|
private readonly IQueryAuthorizationService _authorizationService;
|
||||||
|
|
||||||
|
public QueryControllerAsyncAuthorizationFilter(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_authorizationService = serviceProvider.GetService<IQueryAuthorizationService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||||
|
{
|
||||||
|
if (_authorizationService == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var action = context.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;
|
||||||
|
if (action == null)
|
||||||
|
throw new Exception("Only Supports controller action descriptor");
|
||||||
|
|
||||||
|
var attribute = action.MethodInfo.GetCustomAttribute<QueryControllerAuthorizationAttribute>();
|
||||||
|
Type queryType;
|
||||||
|
if (attribute?.QueryType != null)
|
||||||
|
queryType = attribute.QueryType;
|
||||||
|
else
|
||||||
|
queryType = action.ControllerTypeInfo.GenericTypeArguments.First();
|
||||||
|
|
||||||
|
var ar = await _authorizationService.IsAllowedAsync(queryType);
|
||||||
|
if (ar == AuthorizationResult.Forbidden)
|
||||||
|
context.Result = new StatusCodeResult(403);
|
||||||
|
else if (ar == AuthorizationResult.Unauthorized)
|
||||||
|
context.Result = new StatusCodeResult(401);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class QueryControllerAuthorizationAttribute : TypeFilterAttribute
|
||||||
|
{
|
||||||
|
public QueryControllerAuthorizationAttribute() : base(typeof(QueryControllerAsyncAuthorizationFilter))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryControllerAuthorizationAttribute(Type queryType) : base(typeof(QueryControllerAsyncAuthorizationFilter))
|
||||||
|
{
|
||||||
|
QueryType = queryType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type QueryType { get; }
|
||||||
|
}
|
27
OpenHarbor.CQRS.AspNetCore/Mvc/QueryControllerConvention.cs
Normal file
27
OpenHarbor.CQRS.AspNetCore/Mvc/QueryControllerConvention.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class QueryControllerConvention : IControllerModelConvention
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public QueryControllerConvention(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply(ControllerModel controller)
|
||||||
|
{
|
||||||
|
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("QueryController") && controller.ControllerType.Assembly == typeof(QueryControllerConvention).Assembly)
|
||||||
|
{
|
||||||
|
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
||||||
|
var queryDiscovery = _serviceProvider.GetRequiredService<IQueryDiscovery>();
|
||||||
|
var query = queryDiscovery.FindQuery(genericType);
|
||||||
|
controller.ControllerName = query.LowerCamelCaseName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
using OpenHarbor.CQRS.AspNetCore.Abstractions.Attributes;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class QueryControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
||||||
|
{
|
||||||
|
private readonly ServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public QueryControllerFeatureProvider(ServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
|
||||||
|
{
|
||||||
|
var queryDiscovery = this._serviceProvider.GetRequiredService<IQueryDiscovery>();
|
||||||
|
foreach (var f in queryDiscovery.GetQueries())
|
||||||
|
{
|
||||||
|
var ignoreAttribute = f.QueryType.GetCustomAttribute<QueryControllerIgnoreAttribute>();
|
||||||
|
if (ignoreAttribute != null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (f.Category != "BasicQuery")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var controllerType = typeof(QueryController<,>).MakeGenericType(f.QueryType, f.QueryResultType);
|
||||||
|
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||||
|
feature.Controllers.Add(controllerTypeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
OpenHarbor.CQRS.AspNetCore/Mvc/QueryControllerOptions.cs
Normal file
6
OpenHarbor.CQRS.AspNetCore/Mvc/QueryControllerOptions.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class QueryControllerOptions
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
19
OpenHarbor.CQRS.AspNetCore/OpenHarbor.CQRS.AspNetCore.csproj
Normal file
19
OpenHarbor.CQRS.AspNetCore/OpenHarbor.CQRS.AspNetCore.csproj
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
|
||||||
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS.Abstractions\OpenHarbor.CQRS.Abstractions.csproj" />
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS.AspNetCore.Abstractions\OpenHarbor.CQRS.AspNetCore.Abstractions.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
|
||||||
|
public class DynamicQueryInterceptorProvider<TSource, TDestination> : IDynamicQueryInterceptorProvider<TSource, TDestination>
|
||||||
|
{
|
||||||
|
private readonly Type[] _types;
|
||||||
|
|
||||||
|
public DynamicQueryInterceptorProvider(params Type[] types)
|
||||||
|
{
|
||||||
|
_types = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Type> GetInterceptorsTypes()
|
||||||
|
{
|
||||||
|
return _types;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
|
||||||
|
public interface IAlterQueryableService<TSource, TDestination>
|
||||||
|
{
|
||||||
|
Task<IQueryable<TSource>> AlterQueryableAsync(IQueryable<TSource> query, IDynamicQuery dynamicQuery, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IAlterQueryableService<TSource, TDestination, in TParams>
|
||||||
|
where TParams : class
|
||||||
|
{
|
||||||
|
Task<IQueryable<TSource>> AlterQueryableAsync(IQueryable<TSource> query, IDynamicQueryParams<TParams> dynamicQuery, CancellationToken cancellationToken = default);
|
||||||
|
}
|
29
OpenHarbor.CQRS.DynamicQuery.Abstractions/IDynamicQuery.cs
Normal file
29
OpenHarbor.CQRS.DynamicQuery.Abstractions/IDynamicQuery.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
|
||||||
|
public interface IDynamicQuery<TSource, TDestination> : IDynamicQuery
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDynamicQuery<TSource, TDestination, out TParams> : IDynamicQuery<TSource, TDestination>, IDynamicQueryParams<TParams>
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
where TParams : class
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDynamicQuery
|
||||||
|
{
|
||||||
|
List<IFilter> GetFilters();
|
||||||
|
List<IGroup> GetGroups();
|
||||||
|
List<ISort> GetSorts();
|
||||||
|
List<IAggregate> GetAggregates();
|
||||||
|
int? GetPage();
|
||||||
|
int? GetPageSize();
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
|
||||||
|
public interface IDynamicQueryInterceptorProvider<TSource, TDestination>
|
||||||
|
{
|
||||||
|
IEnumerable<Type> GetInterceptorsTypes();
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
|
||||||
|
public interface IDynamicQueryParams<out TParams>
|
||||||
|
where TParams : class
|
||||||
|
{
|
||||||
|
TParams GetParams();
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
|
||||||
|
public interface IQueryableProvider<TSource>
|
||||||
|
{
|
||||||
|
Task<IQueryable<TSource>> GetQueryableAsync(object query, CancellationToken cancellationToken = default);
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
|
||||||
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="PoweredSoft.DynamicQuery.Core" Version="3.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
68
OpenHarbor.CQRS.DynamicQuery.AspNetCore/DynamicQuery.cs
Normal file
68
OpenHarbor.CQRS.DynamicQuery.AspNetCore/DynamicQuery.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
using PoweredSoft.DynamicQuery;
|
||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore;
|
||||||
|
|
||||||
|
public class DynamicQuery<TSource, TDestination> : DynamicQuery, IDynamicQuery<TSource, TDestination>
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DynamicQuery<TSource, TDestination, TParams> : DynamicQuery, IDynamicQuery<TSource, TDestination, TParams>
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
where TParams : class
|
||||||
|
{
|
||||||
|
public TParams Params { get; set; }
|
||||||
|
|
||||||
|
public TParams GetParams()
|
||||||
|
{
|
||||||
|
return Params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DynamicQuery : IDynamicQuery
|
||||||
|
{
|
||||||
|
public int? Page { get; set; }
|
||||||
|
public int? PageSize { get; set; }
|
||||||
|
public List<Sort> Sorts { get; set; }
|
||||||
|
public List<DynamicQueryAggregate> Aggregates { get; set; }
|
||||||
|
public List<Group> Groups { get; set; }
|
||||||
|
public List<DynamicQueryFilter> Filters { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public List<IAggregate> GetAggregates()
|
||||||
|
{
|
||||||
|
return Aggregates?.Select(t => t.ToAggregate())?.ToList();//.AsEnumerable<IAggregate>()?.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IFilter> GetFilters()
|
||||||
|
{
|
||||||
|
return Filters?.Select(t => t.ToFilter())?.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IGroup> GetGroups()
|
||||||
|
{
|
||||||
|
return this.Groups?.AsEnumerable<IGroup>()?.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? GetPage()
|
||||||
|
{
|
||||||
|
return this.Page;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? GetPageSize()
|
||||||
|
{
|
||||||
|
return this.PageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ISort> GetSorts()
|
||||||
|
{
|
||||||
|
return this.Sorts?.AsEnumerable<ISort>()?.ToList();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
using PoweredSoft.DynamicQuery;
|
||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore;
|
||||||
|
|
||||||
|
public class DynamicQueryAggregate
|
||||||
|
{
|
||||||
|
public string Path { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
|
public IAggregate ToAggregate()
|
||||||
|
{
|
||||||
|
return new Aggregate
|
||||||
|
{
|
||||||
|
Type = Enum.Parse<AggregateType>(Type),
|
||||||
|
Path = Path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using PoweredSoft.DynamicQuery;
|
||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore;
|
||||||
|
|
||||||
|
public class DynamicQueryFilter
|
||||||
|
{
|
||||||
|
public List<DynamicQueryFilter> Filters { get; set; }
|
||||||
|
public bool? And { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public bool? Not { get; set; }
|
||||||
|
public string Path { get; set; }
|
||||||
|
public object Value { get; set; }
|
||||||
|
|
||||||
|
[FromQuery(Name ="value")]
|
||||||
|
public string QueryValue
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool? CaseInsensitive { get; set; }
|
||||||
|
|
||||||
|
public IFilter ToFilter()
|
||||||
|
{
|
||||||
|
var type = Enum.Parse<FilterType>(Type);
|
||||||
|
if (type == FilterType.Composite)
|
||||||
|
{
|
||||||
|
var compositeFilter = new CompositeFilter
|
||||||
|
{
|
||||||
|
And = And,
|
||||||
|
Type = FilterType.Composite,
|
||||||
|
Filters = Filters?.Select(t => t.ToFilter())?.ToList() ?? new List<IFilter>()
|
||||||
|
};
|
||||||
|
return compositeFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
object value = Value;
|
||||||
|
if (Value is JsonElement jsonElement)
|
||||||
|
{
|
||||||
|
switch (jsonElement.ValueKind)
|
||||||
|
{
|
||||||
|
case JsonValueKind.String:
|
||||||
|
value = jsonElement.ToString();
|
||||||
|
break;
|
||||||
|
case JsonValueKind.Number:
|
||||||
|
if (jsonElement.ToString().Contains('.'))
|
||||||
|
value = jsonElement.GetDecimal();
|
||||||
|
else if (jsonElement.TryGetInt64(out var convertedValue))
|
||||||
|
value = convertedValue;
|
||||||
|
break;
|
||||||
|
case JsonValueKind.True:
|
||||||
|
value = true;
|
||||||
|
break;
|
||||||
|
case JsonValueKind.False:
|
||||||
|
value = false;
|
||||||
|
break;
|
||||||
|
// TODO: Array support
|
||||||
|
default:
|
||||||
|
value = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var simpleFilter = new SimpleFilter
|
||||||
|
{
|
||||||
|
And = And,
|
||||||
|
Type = type,
|
||||||
|
Not = Not,
|
||||||
|
Path = Path,
|
||||||
|
Value = value,
|
||||||
|
CaseInsensitive = CaseInsensitive,
|
||||||
|
};
|
||||||
|
return simpleFilter;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using OpenHarbor.CQRS.Abstractions;
|
||||||
|
using OpenHarbor.CQRS.AspNetCore.Mvc;
|
||||||
|
using OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
[ApiController, Route("api/query/[controller]")]
|
||||||
|
public class DynamicQueryController<TSource, TDestination> : Controller
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
{
|
||||||
|
[HttpPost, QueryControllerAuthorization]
|
||||||
|
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(
|
||||||
|
[FromBody] DynamicQuery<TSource, TDestination> query,
|
||||||
|
[FromServices]IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>> queryHandler
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet, QueryControllerAuthorization]
|
||||||
|
public async Task<IQueryExecutionResult<TDestination>> HandleGetAsync(
|
||||||
|
[FromQuery] DynamicQuery<TSource, TDestination> query,
|
||||||
|
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>> queryHandler
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiController, Route("api/query/[controller]")]
|
||||||
|
public class DynamicQueryController<TSource, TDestination, TParams> : Controller
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
where TParams : class
|
||||||
|
{
|
||||||
|
[HttpPost, QueryControllerAuthorization]
|
||||||
|
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(
|
||||||
|
[FromBody] DynamicQuery<TSource, TDestination, TParams> query,
|
||||||
|
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>> queryHandler
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet, QueryControllerAuthorization]
|
||||||
|
public async Task<IQueryExecutionResult<TDestination>> HandleGetAsync(
|
||||||
|
[FromQuery] DynamicQuery<TSource, TDestination, TParams> query,
|
||||||
|
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>> queryHandler
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class DynamicQueryControllerConvention : IControllerModelConvention
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public DynamicQueryControllerConvention(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply(ControllerModel controller)
|
||||||
|
{
|
||||||
|
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("DynamicQueryController") && controller.ControllerType.Assembly == typeof(DynamicQueryControllerConvention).Assembly)
|
||||||
|
{
|
||||||
|
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
||||||
|
var queryDiscovery = _serviceProvider.GetRequiredService<IQueryDiscovery>();
|
||||||
|
var query = queryDiscovery.FindQuery(genericType);
|
||||||
|
controller.ControllerName = query.LowerCamelCaseName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
using OpenHarbor.CQRS.AspNetCore.Abstractions.Attributes;
|
||||||
|
using OpenHarbor.CQRS.DynamicQuery.Discover;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class DynamicQueryControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
||||||
|
{
|
||||||
|
private readonly ServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public DynamicQueryControllerFeatureProvider(ServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
|
||||||
|
{
|
||||||
|
var queryDiscovery = _serviceProvider.GetRequiredService<IQueryDiscovery>();
|
||||||
|
foreach (var queryMeta in queryDiscovery.GetQueries())
|
||||||
|
{
|
||||||
|
var ignoreAttribute = queryMeta.QueryType.GetCustomAttribute<QueryControllerIgnoreAttribute>();
|
||||||
|
if (ignoreAttribute != null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (queryMeta.Category != "DynamicQuery")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (queryMeta is DynamicQueryMeta dynamicQueryMeta)
|
||||||
|
{
|
||||||
|
if (dynamicQueryMeta.ParamsType == null)
|
||||||
|
{
|
||||||
|
var controllerType = typeof(DynamicQueryController<,>).MakeGenericType(queryMeta.QueryType, dynamicQueryMeta.SourceType, dynamicQueryMeta.DestinationType);
|
||||||
|
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||||
|
feature.Controllers.Add(controllerTypeInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var controllerType = typeof(DynamicQueryController<,,>).MakeGenericType(queryMeta.QueryType, dynamicQueryMeta.SourceType, dynamicQueryMeta.DestinationType, dynamicQueryMeta.ParamsType);
|
||||||
|
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||||
|
feature.Controllers.Add(controllerTypeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
public class DynamicQueryControllerOptions
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using OpenHarbor.CQRS.DynamicQuery.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.AspNetCore;
|
||||||
|
|
||||||
|
public static class MvcBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IMvcBuilder AddPoweredSoftDynamicQueries(this IMvcBuilder builder, Action<DynamicQueryControllerOptions> configuration = null)
|
||||||
|
{
|
||||||
|
var options = new DynamicQueryControllerOptions();
|
||||||
|
configuration?.Invoke(options);
|
||||||
|
var services = builder.Services;
|
||||||
|
var serviceProvider = services.BuildServiceProvider();
|
||||||
|
builder.AddMvcOptions(o => o.Conventions.Add(new DynamicQueryControllerConvention(serviceProvider)));
|
||||||
|
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new DynamicQueryControllerFeatureProvider(serviceProvider)));
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
|
||||||
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
|
<IsAotCompatible>true</IsAotCompatible>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS.Abstractions\OpenHarbor.CQRS.Abstractions.csproj" />
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS.AspNetCore.Abstractions\OpenHarbor.CQRS.AspNetCore.Abstractions.csproj" />
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS.AspNetCore\OpenHarbor.CQRS.AspNetCore.csproj" />
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS.DynamicQuery.Abstractions\OpenHarbor.CQRS.DynamicQuery.Abstractions.csproj" />
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS.DynamicQuery\OpenHarbor.CQRS.DynamicQuery.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
32
OpenHarbor.CQRS.DynamicQuery/Discover/DynamicQueryMeta.cs
Normal file
32
OpenHarbor.CQRS.DynamicQuery/Discover/DynamicQueryMeta.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using Pluralize.NET;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery.Discover;
|
||||||
|
|
||||||
|
public class DynamicQueryMeta : QueryMeta
|
||||||
|
{
|
||||||
|
public DynamicQueryMeta(Type queryType, Type serviceType, Type queryResultType) : base(queryType, serviceType, queryResultType)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type SourceType => QueryType.GetGenericArguments()[0];
|
||||||
|
public Type DestinationType => QueryType.GetGenericArguments()[1];
|
||||||
|
public override string Category => "DynamicQuery";
|
||||||
|
public override string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (OverridableName != null)
|
||||||
|
return OverridableName;
|
||||||
|
|
||||||
|
var pluralizer = new Pluralizer();
|
||||||
|
return pluralizer.Pluralize(DestinationType.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type ParamsType { get; internal set; }
|
||||||
|
public string OverridableName { get; internal set; }
|
||||||
|
}
|
||||||
|
|
67
OpenHarbor.CQRS.DynamicQuery/DynamicQueryHandler.cs
Normal file
67
OpenHarbor.CQRS.DynamicQuery/DynamicQueryHandler.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery;
|
||||||
|
|
||||||
|
public class DynamicQueryHandler<TSource, TDestination>
|
||||||
|
: DynamicQueryHandlerBase<TSource, TDestination>,
|
||||||
|
OpenHarbor.CQRS.Abstractions.IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>>
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
{
|
||||||
|
public DynamicQueryHandler(IQueryHandlerAsync queryHandlerAsync,
|
||||||
|
IEnumerable<IQueryableProvider<TSource>> queryableProviders,
|
||||||
|
IEnumerable<IAlterQueryableService<TSource, TDestination>> alterQueryableServices,
|
||||||
|
IEnumerable<IDynamicQueryInterceptorProvider<TSource, TDestination>> dynamicQueryInterceptorProviders,
|
||||||
|
IServiceProvider serviceProvider) : base(queryHandlerAsync, queryableProviders, alterQueryableServices, dynamicQueryInterceptorProviders, serviceProvider)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IQueryExecutionResult<TDestination>> HandleAsync(IDynamicQuery<TSource, TDestination> query, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return ProcessQueryAsync(query, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DynamicQueryHandler<TSource, TDestination, TParams>
|
||||||
|
: DynamicQueryHandlerBase<TSource, TDestination>,
|
||||||
|
OpenHarbor.CQRS.Abstractions.IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>>
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
where TParams : class
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<IAlterQueryableService<TSource, TDestination, TParams>> alterQueryableServicesWithParams;
|
||||||
|
|
||||||
|
public DynamicQueryHandler(IQueryHandlerAsync queryHandlerAsync,
|
||||||
|
IEnumerable<IQueryableProvider<TSource>> queryableProviders,
|
||||||
|
IEnumerable<IAlterQueryableService<TSource, TDestination>> alterQueryableServices,
|
||||||
|
IEnumerable<IAlterQueryableService<TSource, TDestination, TParams>> alterQueryableServicesWithParams,
|
||||||
|
IEnumerable<IDynamicQueryInterceptorProvider<TSource, TDestination>> dynamicQueryInterceptorProviders,
|
||||||
|
IServiceProvider serviceProvider) : base(queryHandlerAsync, queryableProviders, alterQueryableServices, dynamicQueryInterceptorProviders, serviceProvider)
|
||||||
|
{
|
||||||
|
this.alterQueryableServicesWithParams = alterQueryableServicesWithParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<IQueryable<TSource>> AlterSourceAsync(IQueryable<TSource> source, IDynamicQuery query, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
source = await base.AlterSourceAsync(source, query, cancellationToken);
|
||||||
|
|
||||||
|
if (query is IDynamicQueryParams<TParams> withParams)
|
||||||
|
{
|
||||||
|
foreach (var it in alterQueryableServicesWithParams)
|
||||||
|
source = await it.AlterQueryableAsync(source, withParams, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IQueryExecutionResult<TDestination>> HandleAsync(IDynamicQuery<TSource, TDestination, TParams> query, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return this.ProcessQueryAsync(query, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
92
OpenHarbor.CQRS.DynamicQuery/DynamicQueryHandlerBase.cs
Normal file
92
OpenHarbor.CQRS.DynamicQuery/DynamicQueryHandlerBase.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
using PoweredSoft.DynamicQuery;
|
||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery;
|
||||||
|
|
||||||
|
public abstract class DynamicQueryHandlerBase<TSource, TDestination>
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
{
|
||||||
|
private readonly IQueryHandlerAsync _queryHandlerAsync;
|
||||||
|
private readonly IEnumerable<IQueryableProvider<TSource>> _queryableProviders;
|
||||||
|
private readonly IEnumerable<IAlterQueryableService<TSource, TDestination>> _alterQueryableServices;
|
||||||
|
private readonly IEnumerable<IDynamicQueryInterceptorProvider<TSource, TDestination>> _dynamicQueryInterceptorProviders;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public DynamicQueryHandlerBase(IQueryHandlerAsync queryHandlerAsync,
|
||||||
|
IEnumerable<IQueryableProvider<TSource>> queryableProviders,
|
||||||
|
IEnumerable<IAlterQueryableService<TSource, TDestination>> alterQueryableServices,
|
||||||
|
IEnumerable<IDynamicQueryInterceptorProvider<TSource, TDestination>> dynamicQueryInterceptorProviders,
|
||||||
|
IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_queryHandlerAsync = queryHandlerAsync;
|
||||||
|
_queryableProviders = queryableProviders;
|
||||||
|
_alterQueryableServices = alterQueryableServices;
|
||||||
|
_dynamicQueryInterceptorProviders = dynamicQueryInterceptorProviders;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual Task<IQueryable<TSource>> GetQueryableAsync(IDynamicQuery query, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (_queryableProviders.Any())
|
||||||
|
return _queryableProviders.ElementAt(0).GetQueryableAsync(query, cancellationToken);
|
||||||
|
|
||||||
|
throw new Exception($"You must provide a QueryableProvider<TSource> for {typeof(TSource).Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IQueryExecutionOptions GetQueryExecutionOptions(IQueryable<TSource> source, IDynamicQuery query)
|
||||||
|
{
|
||||||
|
return new QueryExecutionOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<IQueryInterceptor> GetInterceptors()
|
||||||
|
{
|
||||||
|
var types = _dynamicQueryInterceptorProviders.SelectMany(t => t.GetInterceptorsTypes()).Distinct();
|
||||||
|
foreach (var type in types)
|
||||||
|
yield return _serviceProvider.GetService(type) as IQueryInterceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<IQueryExecutionResult<TDestination>> ProcessQueryAsync(IDynamicQuery query, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var source = await GetQueryableAsync(query, cancellationToken);
|
||||||
|
source = await AlterSourceAsync(source, query, cancellationToken);
|
||||||
|
var options = GetQueryExecutionOptions(source, query);
|
||||||
|
var interceptors = GetInterceptors();
|
||||||
|
|
||||||
|
foreach (var interceptor in interceptors)
|
||||||
|
_queryHandlerAsync.AddInterceptor(interceptor);
|
||||||
|
|
||||||
|
var criteria = CreateCriteriaFromQuery(query);
|
||||||
|
var result = await _queryHandlerAsync.ExecuteAsync<TSource, TDestination>(source, criteria, options, cancellationToken);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual async Task<IQueryable<TSource>> AlterSourceAsync(IQueryable<TSource> source, IDynamicQuery query, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
foreach (var t in _alterQueryableServices)
|
||||||
|
source = await t.AlterQueryableAsync(source, query, cancellationToken);
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual IQueryCriteria CreateCriteriaFromQuery(IDynamicQuery query)
|
||||||
|
{
|
||||||
|
var criteria = new QueryCriteria
|
||||||
|
{
|
||||||
|
Page = query?.GetPage(),
|
||||||
|
PageSize = query?.GetPageSize(),
|
||||||
|
Filters = query?.GetFilters() ?? new List<IFilter>(),
|
||||||
|
Sorts = query?.GetSorts() ?? new List<ISort>(),
|
||||||
|
Groups = query?.GetGroups() ?? new List<IGroup>(),
|
||||||
|
Aggregates = query?.GetAggregates() ?? new List<IAggregate>()
|
||||||
|
};
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
|
||||||
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Pluralize.NET" Version="1.0.2" />
|
||||||
|
<PackageReference Include="PoweredSoft.DynamicQuery" Version="3.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS.DynamicQuery.Abstractions\OpenHarbor.CQRS.DynamicQuery.Abstractions.csproj" />
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS\OpenHarbor.CQRS.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
175
OpenHarbor.CQRS.DynamicQuery/ServiceCollectionExtensions.cs
Normal file
175
OpenHarbor.CQRS.DynamicQuery/ServiceCollectionExtensions.cs
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using OpenHarbor.CQRS.Abstractions;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
using OpenHarbor.CQRS.DynamicQuery.Abstractions;
|
||||||
|
using OpenHarbor.CQRS.DynamicQuery.Discover;
|
||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.DynamicQuery;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddDynamicQuery<TSourceAndDestination>(this IServiceCollection services, string name = null)
|
||||||
|
where TSourceAndDestination : class
|
||||||
|
=> AddDynamicQuery<TSourceAndDestination, TSourceAndDestination>(services, name: name);
|
||||||
|
|
||||||
|
public static IServiceCollection AddDynamicQuery<TSource, TDestination>(this IServiceCollection services, string name = null)
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
{
|
||||||
|
// add query handler.
|
||||||
|
services.AddTransient<IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>>, DynamicQueryHandler<TSource, TDestination>>();
|
||||||
|
|
||||||
|
// add for discovery purposes.
|
||||||
|
var queryType = typeof(IDynamicQuery<TSource, TDestination>);
|
||||||
|
var resultType = typeof(IQueryExecutionResult<TDestination>);
|
||||||
|
var serviceType = typeof(DynamicQueryHandler<TSource, TDestination>);
|
||||||
|
var queryMeta = new DynamicQueryMeta(queryType, serviceType, resultType)
|
||||||
|
{
|
||||||
|
OverridableName = name
|
||||||
|
};
|
||||||
|
|
||||||
|
services.AddSingleton<IQueryMeta>(queryMeta);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddDynamicQueryWithProvider<TSource, TQueryableProvider>(this IServiceCollection services, string name = null)
|
||||||
|
where TQueryableProvider : class, IQueryableProvider<TSource>
|
||||||
|
where TSource : class
|
||||||
|
{
|
||||||
|
services.AddTransient<IQueryableProvider<TSource>, TQueryableProvider>()
|
||||||
|
.AddDynamicQuery<TSource>(name: name);
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddDynamicQueryWithParamsAndProvider<TSource, TParams, TQueryableProvider>(this IServiceCollection services, string name = null)
|
||||||
|
where TQueryableProvider : class, IQueryableProvider<TSource>
|
||||||
|
where TParams : class
|
||||||
|
where TSource : class
|
||||||
|
{
|
||||||
|
services.AddTransient<IQueryableProvider<TSource>, TQueryableProvider>()
|
||||||
|
.AddDynamicQueryWithParams<TSource, TParams>(name: name);
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddDynamicQueryWithParams<TSourceAndDestination, TParams>(this IServiceCollection services, string name = null)
|
||||||
|
where TSourceAndDestination : class
|
||||||
|
where TParams : class
|
||||||
|
=> AddDynamicQueryWithParams<TSourceAndDestination, TSourceAndDestination, TParams>(services, name: name);
|
||||||
|
|
||||||
|
public static IServiceCollection AddDynamicQueryWithParams<TSource, TDestination, TParams>(this IServiceCollection services, string name = null)
|
||||||
|
where TSource : class
|
||||||
|
where TDestination : class
|
||||||
|
where TParams : class
|
||||||
|
{
|
||||||
|
// add query handler.
|
||||||
|
services.AddTransient<IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>>, DynamicQueryHandler<TSource, TDestination, TParams>>();
|
||||||
|
|
||||||
|
// add for discovery purposes.
|
||||||
|
var queryType = typeof(IDynamicQuery<TSource, TDestination, TParams>);
|
||||||
|
var resultType = typeof(IQueryExecutionResult<TDestination>);
|
||||||
|
var serviceType = typeof(DynamicQueryHandler<TSource, TDestination>);
|
||||||
|
var queryMeta = new DynamicQueryMeta(queryType, serviceType, resultType)
|
||||||
|
{
|
||||||
|
|
||||||
|
// params type.
|
||||||
|
ParamsType = typeof(TParams),
|
||||||
|
OverridableName = name
|
||||||
|
};
|
||||||
|
|
||||||
|
services.AddSingleton<IQueryMeta>(queryMeta);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddAlterQueryable<TSourceAndDestination, TService>(this IServiceCollection services)
|
||||||
|
where TService : class, IAlterQueryableService<TSourceAndDestination, TSourceAndDestination>
|
||||||
|
{
|
||||||
|
return services.AddTransient<IAlterQueryableService<TSourceAndDestination, TSourceAndDestination>, TService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddAlterQueryable<TSource, TDestination, TService>(this IServiceCollection services)
|
||||||
|
where TService : class, IAlterQueryableService<TSource, TDestination>
|
||||||
|
{
|
||||||
|
return services.AddTransient<IAlterQueryableService<TSource, TDestination>, TService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddAlterQueryableWithParams<TSourceAndTDestination, TParams, TService>
|
||||||
|
(this IServiceCollection services)
|
||||||
|
where TParams : class
|
||||||
|
where TService : class, IAlterQueryableService<TSourceAndTDestination, TSourceAndTDestination, TParams>
|
||||||
|
{
|
||||||
|
return services.AddTransient<IAlterQueryableService< TSourceAndTDestination, TSourceAndTDestination, TParams>, TService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddAlterQueryableWithParams<TSource, TDestination, TParams, TService>
|
||||||
|
(this IServiceCollection services)
|
||||||
|
where TParams : class
|
||||||
|
where TService : class, IAlterQueryableService<TSource, TDestination, TParams>
|
||||||
|
{
|
||||||
|
return services.AddTransient<IAlterQueryableService<TSource, TDestination, TParams>, TService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddDynamicQueryInterceptor<TSource, TDestination, TInterceptor>(this IServiceCollection services)
|
||||||
|
where TInterceptor : class, IQueryInterceptor
|
||||||
|
{
|
||||||
|
services.TryAddTransient<TInterceptor>();
|
||||||
|
return services.AddSingleton<IDynamicQueryInterceptorProvider<TSource, TDestination>>(
|
||||||
|
new DynamicQueryInterceptorProvider<TSource, TDestination>(typeof(TInterceptor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddDynamicQueryInterceptors<TSource, TDestination, T1, T2>(this IServiceCollection services)
|
||||||
|
where T1 : class, IQueryInterceptor
|
||||||
|
where T2 : class, IQueryInterceptor
|
||||||
|
{
|
||||||
|
services.TryAddTransient<T1>();
|
||||||
|
services.TryAddTransient<T2>();
|
||||||
|
return services.AddSingleton<IDynamicQueryInterceptorProvider<TSource, TDestination>>(
|
||||||
|
new DynamicQueryInterceptorProvider<TSource, TDestination>(typeof(T1), typeof(T2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddDynamicQueryInterceptors<TSource, TDestination, T1, T2, T3>(this IServiceCollection services)
|
||||||
|
where T1 : class, IQueryInterceptor
|
||||||
|
where T2 : class, IQueryInterceptor
|
||||||
|
where T3 : class, IQueryInterceptor
|
||||||
|
{
|
||||||
|
services.TryAddTransient<T1>();
|
||||||
|
services.TryAddTransient<T2>();
|
||||||
|
services.TryAddTransient<T3>();
|
||||||
|
return services.AddSingleton<IDynamicQueryInterceptorProvider<TSource, TDestination>>(
|
||||||
|
new DynamicQueryInterceptorProvider<TSource, TDestination>(typeof(T1), typeof(T2), typeof(T3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddDynamicQueryInterceptors<TSource, TDestination, T1, T2, T3, T4>(this IServiceCollection services)
|
||||||
|
where T1 : class, IQueryInterceptor
|
||||||
|
where T2 : class, IQueryInterceptor
|
||||||
|
where T3 : class, IQueryInterceptor
|
||||||
|
where T4 : class, IQueryInterceptor
|
||||||
|
{
|
||||||
|
services.TryAddTransient<T1>();
|
||||||
|
services.TryAddTransient<T2>();
|
||||||
|
services.TryAddTransient<T3>();
|
||||||
|
services.TryAddTransient<T4>();
|
||||||
|
return services.AddSingleton<IDynamicQueryInterceptorProvider<TSource, TDestination>>(
|
||||||
|
new DynamicQueryInterceptorProvider<TSource, TDestination>(typeof(T1), typeof(T2), typeof(T3), typeof(T4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddDynamicQueryInterceptors<TSource, TDestination, T1, T2, T3, T4, T5>(this IServiceCollection services)
|
||||||
|
where T1 : class, IQueryInterceptor
|
||||||
|
where T2 : class, IQueryInterceptor
|
||||||
|
where T3 : class, IQueryInterceptor
|
||||||
|
where T4 : class, IQueryInterceptor
|
||||||
|
where T5 : class, IQueryInterceptor
|
||||||
|
{
|
||||||
|
services.TryAddTransient<T1>();
|
||||||
|
services.TryAddTransient<T2>();
|
||||||
|
services.TryAddTransient<T3>();
|
||||||
|
services.TryAddTransient<T4>();
|
||||||
|
services.TryAddTransient<T5>();
|
||||||
|
return services.AddSingleton<IDynamicQueryInterceptorProvider<TSource, TDestination>>(
|
||||||
|
new DynamicQueryInterceptorProvider<TSource, TDestination>(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
|
||||||
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FluentValidation" Version="10.4.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS\OpenHarbor.CQRS.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,43 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using OpenHarbor.CQRS.Abstractions;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.FluentValidation;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
private static IServiceCollection AddFluentValidator<T, TValidator>(this IServiceCollection services)
|
||||||
|
where TValidator : class, IValidator<T>
|
||||||
|
{
|
||||||
|
services.AddTransient<IValidator<T>, TValidator>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddCommand<TCommand, TCommandHandler, TValidator>(this IServiceCollection services)
|
||||||
|
where TCommand : class
|
||||||
|
where TCommandHandler : class, ICommandHandler<TCommand>
|
||||||
|
where TValidator : class, IValidator<TCommand>
|
||||||
|
{
|
||||||
|
return services.AddCommand<TCommand, TCommandHandler>()
|
||||||
|
.AddFluentValidator<TCommand, TValidator>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddCommand<TCommand, TCommandResult, TCommandHandler, TValidator>(this IServiceCollection services)
|
||||||
|
where TCommand : class
|
||||||
|
where TCommandHandler : class, ICommandHandler<TCommand, TCommandResult>
|
||||||
|
where TValidator : class, IValidator<TCommand>
|
||||||
|
{
|
||||||
|
return services.AddCommand<TCommand, TCommandResult, TCommandHandler>()
|
||||||
|
.AddFluentValidator<TCommand, TValidator>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddQuery<TQuery, TQueryResult, TQueryHandler, TValidator>(this IServiceCollection services)
|
||||||
|
where TQuery : class
|
||||||
|
where TQueryHandler : class, IQueryHandler<TQuery, TQueryResult>
|
||||||
|
where TValidator : class, IValidator<TQuery>
|
||||||
|
{
|
||||||
|
services.AddQuery<TQuery, TQueryResult, TQueryHandler>()
|
||||||
|
.AddFluentValidator<TQuery, TValidator>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
# Visual Studio Version 16
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 16.0.30907.101
|
VisualStudioVersion = 16.0.30907.101
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.Abstractions", "PoweredSoft.CQRS.Abstractions\PoweredSoft.CQRS.Abstractions.csproj", "{ED78E19D-31D4-4783-AE9E-2844A8541277}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.Abstractions", "OpenHarbor.CQRS.Abstractions\OpenHarbor.CQRS.Abstractions.csproj", "{ED78E19D-31D4-4783-AE9E-2844A8541277}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS", "PoweredSoft.CQRS\PoweredSoft.CQRS.csproj", "{7069B98F-8736-4114-8AF5-1ACE094E6238}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS", "OpenHarbor.CQRS\OpenHarbor.CQRS.csproj", "{7069B98F-8736-4114-8AF5-1ACE094E6238}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.AspNetCore", "PoweredSoft.CQRS.AspNetCore\PoweredSoft.CQRS.AspNetCore.csproj", "{A1D577E5-61BD-4E25-B2C8-1005C1D7665B}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.AspNetCore", "OpenHarbor.CQRS.AspNetCore\OpenHarbor.CQRS.AspNetCore.csproj", "{A1D577E5-61BD-4E25-B2C8-1005C1D7665B}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.AspNetCore.Abstractions", "PoweredSoft.CQRS.AspNetCore.Abstractions\PoweredSoft.CQRS.AspNetCore.Abstractions.csproj", "{4C466827-31D3-4081-A751-C2FC7C381D7E}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.AspNetCore.Abstractions", "OpenHarbor.CQRS.AspNetCore.Abstractions\OpenHarbor.CQRS.AspNetCore.Abstractions.csproj", "{4C466827-31D3-4081-A751-C2FC7C381D7E}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{617BA357-1A1F-40C5-B19A-A65A960E6142}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{617BA357-1A1F-40C5-B19A-A65A960E6142}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
@ -17,13 +17,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
README.md = README.md
|
README.md = README.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.DynamicQuery", "PoweredSoft.CQRS.DynamicQuery\PoweredSoft.CQRS.DynamicQuery.csproj", "{A38CE930-191F-417C-B5BE-8CC62DB47513}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.DynamicQuery", "OpenHarbor.CQRS.DynamicQuery\OpenHarbor.CQRS.DynamicQuery.csproj", "{A38CE930-191F-417C-B5BE-8CC62DB47513}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.DynamicQuery.Abstractions", "PoweredSoft.CQRS.DynamicQuery.Abstractions\PoweredSoft.CQRS.DynamicQuery.Abstractions.csproj", "{60B5E255-77B8-48E0-AE8F-04E8332970F9}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.DynamicQuery.AspNetCore", "OpenHarbor.CQRS.DynamicQuery.AspNetCore\OpenHarbor.CQRS.DynamicQuery.AspNetCore.csproj", "{0829B99A-0A20-4CAC-A91E-FB67E18444DE}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.DynamicQuery.AspNetCore", "PoweredSoft.CQRS.DynamicQuery.AspNetCore\PoweredSoft.CQRS.DynamicQuery.AspNetCore.csproj", "{0829B99A-0A20-4CAC-A91E-FB67E18444DE}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHarbor.CQRS.FluentValidation", "OpenHarbor.CQRS.FluentValidation\OpenHarbor.CQRS.FluentValidation.csproj", "{70BD37C4-7497-474D-9A40-A701203971D8}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.CQRS.FluentValidation", "PoweredSoft.CQRS.FluentValidation\PoweredSoft.CQRS.FluentValidation.csproj", "{70BD37C4-7497-474D-9A40-A701203971D8}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenHarbor.CQRS.DynamicQuery.Abstractions", "OpenHarbor.CQRS.DynamicQuery.Abstractions\OpenHarbor.CQRS.DynamicQuery.Abstractions.csproj", "{8B9F8ACE-10EA-4215-9776-DE29EC93B020}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -51,10 +51,6 @@ Global
|
|||||||
{A38CE930-191F-417C-B5BE-8CC62DB47513}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{A38CE930-191F-417C-B5BE-8CC62DB47513}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{A38CE930-191F-417C-B5BE-8CC62DB47513}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{A38CE930-191F-417C-B5BE-8CC62DB47513}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{A38CE930-191F-417C-B5BE-8CC62DB47513}.Release|Any CPU.Build.0 = Release|Any CPU
|
{A38CE930-191F-417C-B5BE-8CC62DB47513}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{60B5E255-77B8-48E0-AE8F-04E8332970F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{60B5E255-77B8-48E0-AE8F-04E8332970F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{60B5E255-77B8-48E0-AE8F-04E8332970F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{60B5E255-77B8-48E0-AE8F-04E8332970F9}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{0829B99A-0A20-4CAC-A91E-FB67E18444DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{0829B99A-0A20-4CAC-A91E-FB67E18444DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{0829B99A-0A20-4CAC-A91E-FB67E18444DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{0829B99A-0A20-4CAC-A91E-FB67E18444DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{0829B99A-0A20-4CAC-A91E-FB67E18444DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0829B99A-0A20-4CAC-A91E-FB67E18444DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@ -63,6 +59,10 @@ Global
|
|||||||
{70BD37C4-7497-474D-9A40-A701203971D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{70BD37C4-7497-474D-9A40-A701203971D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{70BD37C4-7497-474D-9A40-A701203971D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{70BD37C4-7497-474D-9A40-A701203971D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{70BD37C4-7497-474D-9A40-A701203971D8}.Release|Any CPU.Build.0 = Release|Any CPU
|
{70BD37C4-7497-474D-9A40-A701203971D8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8B9F8ACE-10EA-4215-9776-DE29EC93B020}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8B9F8ACE-10EA-4215-9776-DE29EC93B020}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8B9F8ACE-10EA-4215-9776-DE29EC93B020}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8B9F8ACE-10EA-4215-9776-DE29EC93B020}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
22
OpenHarbor.CQRS/Discovery/CommandDiscovery.cs
Normal file
22
OpenHarbor.CQRS/Discovery/CommandDiscovery.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Discovery;
|
||||||
|
|
||||||
|
public sealed class CommandDiscovery : ICommandDiscovery
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<ICommandMeta> _commandMetas;
|
||||||
|
|
||||||
|
public CommandDiscovery(IEnumerable<ICommandMeta> commandMetas)
|
||||||
|
{
|
||||||
|
_commandMetas = commandMetas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ICommandMeta> GetCommands() => _commandMetas;
|
||||||
|
public ICommandMeta FindCommand(string name) => _commandMetas.FirstOrDefault(t => t.Name == name);
|
||||||
|
public ICommandMeta FindCommand(Type commandType) => _commandMetas.FirstOrDefault(t => t.CommandType == commandType);
|
||||||
|
public bool CommandExists(string name) => _commandMetas.Any(t => t.Name == name);
|
||||||
|
public bool CommandExists(Type commandType) => _commandMetas.Any(t => t.CommandType == commandType);
|
||||||
|
}
|
23
OpenHarbor.CQRS/Discovery/QueryDiscovery.cs
Normal file
23
OpenHarbor.CQRS/Discovery/QueryDiscovery.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS.Discovery;
|
||||||
|
|
||||||
|
public sealed class QueryDiscovery : IQueryDiscovery
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<IQueryMeta> _queryMetas;
|
||||||
|
|
||||||
|
public QueryDiscovery(IEnumerable<IQueryMeta> queryMetas)
|
||||||
|
{
|
||||||
|
_queryMetas = queryMetas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IQueryMeta> GetQueries() => _queryMetas;
|
||||||
|
public IQueryMeta FindQuery(string name) => _queryMetas.FirstOrDefault(t => t.Name == name);
|
||||||
|
public IQueryMeta FindQuery(Type queryType) => _queryMetas.FirstOrDefault(t => t.QueryType == queryType);
|
||||||
|
public bool QueryExists(string name) => _queryMetas.Any(t => t.Name == name);
|
||||||
|
public bool QueryExists(Type queryType) => _queryMetas.Any(t => t.QueryType == queryType);
|
||||||
|
}
|
||||||
|
|
12
OpenHarbor.CQRS/OpenHarbor.CQRS.csproj
Normal file
12
OpenHarbor.CQRS/OpenHarbor.CQRS.csproj
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<PackageIconUrl>https://avatars.githubusercontent.com/u/52874619?v=4</PackageIconUrl>
|
||||||
|
<Authors>David Lebee, Mathias Beaulieu-Duncan</Authors>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OpenHarbor.CQRS.Abstractions\OpenHarbor.CQRS.Abstractions.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
21
OpenHarbor.CQRS/ServiceCollectionExtensions.cs
Normal file
21
OpenHarbor.CQRS/ServiceCollectionExtensions.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using OpenHarbor.CQRS.Abstractions.Discovery;
|
||||||
|
using OpenHarbor.CQRS.Discovery;
|
||||||
|
|
||||||
|
namespace OpenHarbor.CQRS;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddDefaultQueryDiscovery(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.TryAddTransient<IQueryDiscovery, QueryDiscovery>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddDefaultCommandDiscovery(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.TryAddTransient<ICommandDiscovery, CommandDiscovery>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Attributes
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
|
||||||
public class CommandNameAttribute : Attribute
|
|
||||||
{
|
|
||||||
public CommandNameAttribute(string name)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Attributes
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
|
||||||
public class QueryNameAttribute : Attribute
|
|
||||||
{
|
|
||||||
public QueryNameAttribute(string name)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
using PoweredSoft.CQRS.Abstractions.Attributes;
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Discovery
|
|
||||||
{
|
|
||||||
public class CommandMeta : ICommandMeta
|
|
||||||
{
|
|
||||||
public CommandMeta(Type commandType, Type serviceType, Type commandResultType)
|
|
||||||
{
|
|
||||||
CommandType = commandType;
|
|
||||||
ServiceType = serviceType;
|
|
||||||
CommandResultType = commandResultType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandMeta(Type commandType, Type serviceType)
|
|
||||||
{
|
|
||||||
CommandType = commandType;
|
|
||||||
ServiceType = serviceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual CommandNameAttribute NameAttribute => CommandType.GetCustomAttribute<CommandNameAttribute>();
|
|
||||||
|
|
||||||
public virtual string Name
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var name = NameAttribute?.Name ?? CommandType.Name.Replace("Command", string.Empty);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual Type CommandType { get; }
|
|
||||||
public virtual Type ServiceType { get; }
|
|
||||||
public virtual Type CommandResultType { get; }
|
|
||||||
|
|
||||||
public string LowerCamelCaseName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(Name))
|
|
||||||
return Name;
|
|
||||||
|
|
||||||
var name = Name;
|
|
||||||
var firstLetter = Char.ToLowerInvariant(name[0]);
|
|
||||||
var ret = $"{firstLetter}{name.Substring(1)}";
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Discovery
|
|
||||||
{
|
|
||||||
public interface ICommandMeta
|
|
||||||
{
|
|
||||||
string Name { get; }
|
|
||||||
Type CommandType { get; }
|
|
||||||
Type ServiceType { get; }
|
|
||||||
Type CommandResultType { get; }
|
|
||||||
string LowerCamelCaseName { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Discovery
|
|
||||||
{
|
|
||||||
public interface IQueryDiscovery
|
|
||||||
{
|
|
||||||
IQueryMeta FindQuery(string name);
|
|
||||||
IQueryMeta FindQuery(Type queryType);
|
|
||||||
IEnumerable<IQueryMeta> GetQueries();
|
|
||||||
bool QueryExists(string name);
|
|
||||||
bool QueryExists(Type queryType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ICommandDiscovery
|
|
||||||
{
|
|
||||||
bool CommandExists(string name);
|
|
||||||
bool CommandExists(Type commandType);
|
|
||||||
ICommandMeta FindCommand(string name);
|
|
||||||
ICommandMeta FindCommand(Type commandType);
|
|
||||||
IEnumerable<ICommandMeta> GetCommands();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Discovery
|
|
||||||
{
|
|
||||||
public interface IQueryMeta
|
|
||||||
{
|
|
||||||
string Name { get; }
|
|
||||||
Type QueryType { get; }
|
|
||||||
Type ServiceType { get; }
|
|
||||||
Type QueryResultType { get; }
|
|
||||||
string Category { get; }
|
|
||||||
string LowerCamelCaseName { get; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
using PoweredSoft.CQRS.Abstractions.Attributes;
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Discovery
|
|
||||||
{
|
|
||||||
public class QueryMeta : IQueryMeta
|
|
||||||
{
|
|
||||||
public QueryMeta(Type queryType, Type serviceType, Type queryResultType)
|
|
||||||
{
|
|
||||||
QueryType = queryType;
|
|
||||||
ServiceType = serviceType;
|
|
||||||
QueryResultType = queryResultType;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual QueryNameAttribute NameAttribute => QueryType.GetCustomAttribute<QueryNameAttribute>();
|
|
||||||
|
|
||||||
public virtual string Name
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var name = NameAttribute?.Name ?? QueryType.Name.Replace("Query", string.Empty);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual Type QueryType { get; }
|
|
||||||
public virtual Type ServiceType { get; }
|
|
||||||
public virtual Type QueryResultType { get; }
|
|
||||||
public virtual string Category => "BasicQuery";
|
|
||||||
|
|
||||||
public string LowerCamelCaseName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(Name))
|
|
||||||
return Name;
|
|
||||||
|
|
||||||
var name = Name;
|
|
||||||
var firstLetter = Char.ToLowerInvariant(name[0]);
|
|
||||||
var ret = $"{firstLetter}{name.Substring(1)}";
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions
|
|
||||||
{
|
|
||||||
public interface ICommandHandler<in TCommand>
|
|
||||||
where TCommand : class
|
|
||||||
{
|
|
||||||
Task HandleAsync(TCommand command, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ICommandHandler<in TCommand, TCommandResult>
|
|
||||||
where TCommand : class
|
|
||||||
{
|
|
||||||
Task<TCommandResult> HandleAsync(TCommand command, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions
|
|
||||||
{
|
|
||||||
public interface IQueryHandler<in TQuery, TQueryResult>
|
|
||||||
where TQuery : class
|
|
||||||
{
|
|
||||||
Task<TQueryResult> HandleAsync(TQuery query, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
namespace PoweredSoft.CQRS.Abstractions.Security
|
|
||||||
{
|
|
||||||
public enum AuthorizationResult
|
|
||||||
{
|
|
||||||
Unauthorized,
|
|
||||||
Forbidden,
|
|
||||||
Allowed
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Security
|
|
||||||
{
|
|
||||||
public interface ICommandAuthorizationService
|
|
||||||
{
|
|
||||||
Task<AuthorizationResult> IsAllowedAsync(Type commandType, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Security
|
|
||||||
{
|
|
||||||
|
|
||||||
public interface IQueryAuthorizationService
|
|
||||||
{
|
|
||||||
Task<AuthorizationResult> IsAllowedAsync(Type queryType, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using PoweredSoft.CQRS.Abstractions.Discovery;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions
|
|
||||||
{
|
|
||||||
public static class ServiceCollectionExtensions
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddQuery<TQuery, TQueryResult, TQueryHandler>(this IServiceCollection services)
|
|
||||||
where TQuery : class
|
|
||||||
where TQueryHandler : class, IQueryHandler<TQuery, TQueryResult>
|
|
||||||
{
|
|
||||||
// add handler to DI.
|
|
||||||
services.AddTransient<IQueryHandler<TQuery, TQueryResult>, TQueryHandler>();
|
|
||||||
|
|
||||||
// add for discovery purposes.
|
|
||||||
var queryMeta = new QueryMeta(typeof(TQuery), typeof(IQueryHandler<TQuery, TQueryResult>), typeof(TQueryResult));
|
|
||||||
services.AddSingleton<IQueryMeta>(queryMeta);
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddCommand<TCommand, TCommandResult, TCommandHandler>(this IServiceCollection services)
|
|
||||||
where TCommand : class
|
|
||||||
where TCommandHandler : class, ICommandHandler<TCommand, TCommandResult>
|
|
||||||
{
|
|
||||||
// add handler to DI.
|
|
||||||
services.AddTransient<ICommandHandler<TCommand, TCommandResult>, TCommandHandler>();
|
|
||||||
|
|
||||||
// add for discovery purposes.
|
|
||||||
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>), typeof(TCommandResult));
|
|
||||||
services.AddSingleton<ICommandMeta>(commandMeta);
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddCommand<TCommand, TCommandHandler>(this IServiceCollection services)
|
|
||||||
where TCommand : class
|
|
||||||
where TCommandHandler : class, ICommandHandler<TCommand>
|
|
||||||
{
|
|
||||||
// add handler to DI.
|
|
||||||
services.AddTransient<ICommandHandler<TCommand>, TCommandHandler>();
|
|
||||||
|
|
||||||
// add for discovery purposes.
|
|
||||||
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>));
|
|
||||||
services.AddSingleton<ICommandMeta>(commandMeta);
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
|
||||||
public class CommandControllerIgnoreAttribute : Attribute
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
|
||||||
public class QueryControllerIgnoreAttribute : Attribute
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
|
||||||
<Copyright>Powered Softwares Inc.</Copyright>
|
|
||||||
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;r=g&amp;d=retro</PackageIconUrl>
|
|
||||||
<Company>PoweredSoft</Company>
|
|
||||||
<Authors>PoweredSoft Team</Authors>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
@ -1,39 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using PoweredSoft.CQRS.Abstractions;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
[Produces("application/json")]
|
|
||||||
[ApiController, Route("api/command/[controller]")]
|
|
||||||
public class CommandController<TCommand> : Controller
|
|
||||||
where TCommand : class
|
|
||||||
{
|
|
||||||
[HttpPost, CommandControllerAuthorization]
|
|
||||||
public async Task<IActionResult> Handle([FromServices] ICommandHandler<TCommand> handler,
|
|
||||||
[FromBody] TCommand command)
|
|
||||||
{
|
|
||||||
if (!ModelState.IsValid)
|
|
||||||
return BadRequest(ModelState);
|
|
||||||
|
|
||||||
await handler.HandleAsync(command, this.Request.HttpContext.RequestAborted);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Produces("application/json")]
|
|
||||||
[ApiController, Route("api/command/[controller]")]
|
|
||||||
public class CommandController<TCommand, TTCommandResult> : Controller
|
|
||||||
where TCommand : class
|
|
||||||
{
|
|
||||||
[HttpPost, CommandControllerAuthorization]
|
|
||||||
public async Task<ActionResult<TTCommandResult>> Handle([FromServices] ICommandHandler<TCommand, TTCommandResult> handler,
|
|
||||||
[FromBody] TCommand command)
|
|
||||||
{
|
|
||||||
if (!ModelState.IsValid)
|
|
||||||
return BadRequest(ModelState);
|
|
||||||
|
|
||||||
return Ok(await handler.HandleAsync(command, this.Request.HttpContext.RequestAborted));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Reflection;
|
|
||||||
using PoweredSoft.CQRS.Abstractions.Security;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
public class CommandControllerAsyncAuthorizationFilter : IAsyncAuthorizationFilter
|
|
||||||
{
|
|
||||||
private readonly ICommandAuthorizationService authorizationService;
|
|
||||||
|
|
||||||
public CommandControllerAsyncAuthorizationFilter(IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
authorizationService = serviceProvider.GetService<ICommandAuthorizationService>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
|
||||||
{
|
|
||||||
if (authorizationService == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var action = context.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;
|
|
||||||
if (action == null)
|
|
||||||
throw new Exception("Only Supports controller action descriptor");
|
|
||||||
|
|
||||||
var attribute = action.MethodInfo.GetCustomAttribute<CommandControllerAuthorizationAttribute>();
|
|
||||||
Type commandType;
|
|
||||||
if (attribute?.CommandType != null)
|
|
||||||
commandType = attribute.CommandType;
|
|
||||||
else
|
|
||||||
commandType = action.ControllerTypeInfo.GenericTypeArguments.First();
|
|
||||||
|
|
||||||
var ar = await authorizationService.IsAllowedAsync(commandType);
|
|
||||||
if (ar == AuthorizationResult.Forbidden)
|
|
||||||
context.Result = new StatusCodeResult(403);
|
|
||||||
else if(ar == AuthorizationResult.Unauthorized)
|
|
||||||
context.Result = new StatusCodeResult(401);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
|
||||||
public class CommandControllerAuthorizationAttribute : TypeFilterAttribute
|
|
||||||
{
|
|
||||||
public CommandControllerAuthorizationAttribute() : base(typeof(CommandControllerAsyncAuthorizationFilter))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandControllerAuthorizationAttribute(Type commandType) : base(typeof(CommandControllerAsyncAuthorizationFilter))
|
|
||||||
{
|
|
||||||
CommandType = commandType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type CommandType { get; } = null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using PoweredSoft.CQRS.Abstractions.Discovery;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
public class CommandControllerConvention : IControllerModelConvention
|
|
||||||
{
|
|
||||||
private readonly IServiceProvider serviceProvider;
|
|
||||||
|
|
||||||
public CommandControllerConvention(IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
this.serviceProvider = serviceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Apply(ControllerModel controller)
|
|
||||||
{
|
|
||||||
if (!controller.ControllerType.IsGenericType)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!controller.ControllerType.Name.Contains("CommandController"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (controller.ControllerType.Assembly != typeof(CommandControllerConvention).Assembly)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
|
||||||
var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
|
|
||||||
var command = commandDiscovery.FindCommand(genericType);
|
|
||||||
controller.ControllerName = command.LowerCamelCaseName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using PoweredSoft.CQRS.Abstractions.Discovery;
|
|
||||||
using PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
public class CommandControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
|
||||||
{
|
|
||||||
private readonly ServiceProvider serviceProvider;
|
|
||||||
|
|
||||||
public CommandControllerFeatureProvider(ServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
this.serviceProvider = serviceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
|
|
||||||
{
|
|
||||||
var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
|
|
||||||
foreach (var f in commandDiscovery.GetCommands())
|
|
||||||
{
|
|
||||||
var ignoreAttribute = f.CommandType.GetCustomAttribute<CommandControllerIgnoreAttribute>();
|
|
||||||
if (ignoreAttribute != null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (f.CommandResultType == null)
|
|
||||||
{
|
|
||||||
var controllerType = typeof(CommandController<>).MakeGenericType(f.CommandType);
|
|
||||||
var controllerTypeInfo = controllerType.GetTypeInfo();
|
|
||||||
feature.Controllers.Add(controllerTypeInfo);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var controllerType = typeof(CommandController<,>).MakeGenericType(f.CommandType, f.CommandResultType);
|
|
||||||
var controllerTypeInfo = controllerType.GetTypeInfo();
|
|
||||||
feature.Controllers.Add(controllerTypeInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
public class CommandControllerOptions
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using PoweredSoft.CQRS.Abstractions;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
[Produces("application/json")]
|
|
||||||
[ApiController, Route("api/query/[controller]")]
|
|
||||||
public class QueryController<TQuery, TQueryResult> : Controller
|
|
||||||
where TQuery : class
|
|
||||||
{
|
|
||||||
[HttpPost, QueryControllerAuthorization]
|
|
||||||
public async Task<ActionResult<TQueryResult>> Handle([FromServices] IQueryHandler<TQuery, TQueryResult> handler,
|
|
||||||
[FromBody] TQuery query)
|
|
||||||
{
|
|
||||||
if (!ModelState.IsValid)
|
|
||||||
return BadRequest(ModelState);
|
|
||||||
|
|
||||||
|
|
||||||
return Ok(await handler.HandleAsync(query, this.Request.HttpContext.RequestAborted));
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet, QueryControllerAuthorization]
|
|
||||||
public async Task<ActionResult<TQueryResult>> HandleGet([FromServices] IQueryHandler<TQuery, TQueryResult> handler,
|
|
||||||
[FromQuery] TQuery query)
|
|
||||||
{
|
|
||||||
if (!ModelState.IsValid)
|
|
||||||
return BadRequest(ModelState);
|
|
||||||
|
|
||||||
|
|
||||||
return Ok(await handler.HandleAsync(query, this.Request.HttpContext.RequestAborted));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Reflection;
|
|
||||||
using PoweredSoft.CQRS.Abstractions.Security;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
public class QueryControllerAsyncAuthorizationFilter : IAsyncAuthorizationFilter
|
|
||||||
{
|
|
||||||
private readonly IQueryAuthorizationService authorizationService;
|
|
||||||
|
|
||||||
public QueryControllerAsyncAuthorizationFilter(IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
authorizationService = serviceProvider.GetService<IQueryAuthorizationService>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
|
||||||
{
|
|
||||||
if (authorizationService == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var action = context.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;
|
|
||||||
if (action == null)
|
|
||||||
throw new Exception("Only Supports controller action descriptor");
|
|
||||||
|
|
||||||
var attribute = action.MethodInfo.GetCustomAttribute<QueryControllerAuthorizationAttribute>();
|
|
||||||
Type queryType;
|
|
||||||
if (attribute?.QueryType != null)
|
|
||||||
queryType = attribute.QueryType;
|
|
||||||
else
|
|
||||||
queryType = action.ControllerTypeInfo.GenericTypeArguments.First();
|
|
||||||
|
|
||||||
var ar = await authorizationService.IsAllowedAsync(queryType);
|
|
||||||
if (ar == AuthorizationResult.Forbidden)
|
|
||||||
context.Result = new StatusCodeResult(403);
|
|
||||||
else if (ar == AuthorizationResult.Unauthorized)
|
|
||||||
context.Result = new StatusCodeResult(401);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
|
||||||
public class QueryControllerAuthorizationAttribute : TypeFilterAttribute
|
|
||||||
{
|
|
||||||
public QueryControllerAuthorizationAttribute() : base(typeof(QueryControllerAsyncAuthorizationFilter))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public QueryControllerAuthorizationAttribute(Type queryType) : base(typeof(QueryControllerAsyncAuthorizationFilter))
|
|
||||||
{
|
|
||||||
QueryType = queryType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type QueryType { get; } = null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using PoweredSoft.CQRS.Abstractions.Discovery;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
public class QueryControllerConvention : IControllerModelConvention
|
|
||||||
{
|
|
||||||
private readonly IServiceProvider serviceProvider;
|
|
||||||
|
|
||||||
public QueryControllerConvention(IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
this.serviceProvider = serviceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Apply(ControllerModel controller)
|
|
||||||
{
|
|
||||||
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("QueryController") && controller.ControllerType.Assembly == typeof(QueryControllerConvention).Assembly)
|
|
||||||
{
|
|
||||||
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
|
||||||
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
|
|
||||||
var query = queryDiscovery.FindQuery(genericType);
|
|
||||||
controller.ControllerName = query.LowerCamelCaseName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using PoweredSoft.CQRS.Abstractions.Discovery;
|
|
||||||
using PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
public class QueryControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
|
||||||
{
|
|
||||||
private readonly ServiceProvider serviceProvider;
|
|
||||||
|
|
||||||
public QueryControllerFeatureProvider(ServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
this.serviceProvider = serviceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
|
|
||||||
{
|
|
||||||
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
|
|
||||||
foreach (var f in queryDiscovery.GetQueries())
|
|
||||||
{
|
|
||||||
var ignoreAttribute = f.QueryType.GetCustomAttribute<QueryControllerIgnoreAttribute>();
|
|
||||||
if (ignoreAttribute != null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (f.Category != "BasicQuery")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var controllerType = typeof(QueryController<,>).MakeGenericType(f.QueryType, f.QueryResultType);
|
|
||||||
var controllerTypeInfo = controllerType.GetTypeInfo();
|
|
||||||
feature.Controllers.Add(controllerTypeInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
public class QueryControllerOptions
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
public static class MvcBuilderExtensions
|
|
||||||
{
|
|
||||||
public static IMvcBuilder AddPoweredSoftQueries(this IMvcBuilder builder, Action<QueryControllerOptions> configuration = null)
|
|
||||||
{
|
|
||||||
var options = new QueryControllerOptions();
|
|
||||||
configuration?.Invoke(options);
|
|
||||||
var services = builder.Services;
|
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
|
||||||
builder.AddMvcOptions(o => o.Conventions.Add(new QueryControllerConvention(serviceProvider)));
|
|
||||||
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new QueryControllerFeatureProvider(serviceProvider)));
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IMvcBuilder AddPoweredSoftCommands(this IMvcBuilder builder)
|
|
||||||
{
|
|
||||||
var services = builder.Services;
|
|
||||||
var serviceProvider = services.BuildServiceProvider();
|
|
||||||
builder.AddMvcOptions(o => o.Conventions.Add(new CommandControllerConvention(serviceProvider)));
|
|
||||||
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new CommandControllerFeatureProvider(serviceProvider)));
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<Copyright>Powered Softwares Inc.</Copyright>
|
|
||||||
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;r=g&amp;d=retro</PackageIconUrl>
|
|
||||||
<Company>PoweredSoft</Company>
|
|
||||||
<Authors>PoweredSoft Team</Authors>
|
|
||||||
<IsAotCompatible>true</IsAotCompatible>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\PoweredSoft.CQRS.Abstractions\PoweredSoft.CQRS.Abstractions.csproj" />
|
|
||||||
<ProjectReference Include="..\PoweredSoft.CQRS.AspNetCore.Abstractions\PoweredSoft.CQRS.AspNetCore.Abstractions.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
@ -1,20 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
|
|
||||||
{
|
|
||||||
public class DynamicQueryInterceptorProvider<TSource, TDestination> : IDynamicQueryInterceptorProvider<TSource, TDestination>
|
|
||||||
{
|
|
||||||
private readonly Type[] types;
|
|
||||||
|
|
||||||
public DynamicQueryInterceptorProvider(params Type[] types)
|
|
||||||
{
|
|
||||||
this.types = types;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Type> GetInterceptorsTypes()
|
|
||||||
{
|
|
||||||
return types;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
|
|
||||||
{
|
|
||||||
public interface IAlterQueryableService<TSource, TDestination>
|
|
||||||
{
|
|
||||||
Task<IQueryable<TSource>> AlterQueryableAsync(IQueryable<TSource> query, IDynamicQuery dynamicQuery, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
public interface IAlterQueryableService<TSource, TDestination, TParams>
|
|
||||||
where TParams : class
|
|
||||||
{
|
|
||||||
Task<IQueryable<TSource>> AlterQueryableAsync(IQueryable<TSource> query, IDynamicQueryParams<TParams> dynamicQuery, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using PoweredSoft.DynamicQuery.Core;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
|
|
||||||
{
|
|
||||||
public interface IDynamicQuery<TSource, TDestination> : IDynamicQuery
|
|
||||||
where TSource : class
|
|
||||||
where TDestination : class
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IDynamicQuery<TSource, TDestination, out TParams> : IDynamicQuery<TSource, TDestination>, IDynamicQueryParams<TParams>
|
|
||||||
where TSource : class
|
|
||||||
where TDestination : class
|
|
||||||
where TParams : class
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IDynamicQuery
|
|
||||||
{
|
|
||||||
List<IFilter> GetFilters();
|
|
||||||
List<IGroup> GetGroups();
|
|
||||||
List<ISort> GetSorts();
|
|
||||||
List<IAggregate> GetAggregates();
|
|
||||||
int? GetPage();
|
|
||||||
int? GetPageSize();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
|
|
||||||
{
|
|
||||||
public interface IDynamicQueryInterceptorProvider<TSource, TDestination>
|
|
||||||
{
|
|
||||||
IEnumerable<Type> GetInterceptorsTypes();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
|
|
||||||
{
|
|
||||||
public interface IDynamicQueryParams<out TParams>
|
|
||||||
where TParams : class
|
|
||||||
{
|
|
||||||
TParams GetParams();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.DynamicQuery.Abstractions
|
|
||||||
{
|
|
||||||
public interface IQueryableProvider<TSource>
|
|
||||||
{
|
|
||||||
Task<IQueryable<TSource>> GetQueryableAsync(object query, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
|
||||||
<Copyright>Powered Softwares Inc.</Copyright>
|
|
||||||
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;r=g&amp;d=retro</PackageIconUrl>
|
|
||||||
<Company>PoweredSoft</Company>
|
|
||||||
<Authors>PoweredSoft Team</Authors>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="PoweredSoft.DynamicQuery.Core" Version="3.0.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
@ -1,69 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
|
|
||||||
using PoweredSoft.DynamicQuery;
|
|
||||||
using PoweredSoft.DynamicQuery.Core;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore
|
|
||||||
{
|
|
||||||
public class DynamicQuery<TSource, TDestination> : DynamicQuery, IDynamicQuery<TSource, TDestination>
|
|
||||||
where TSource : class
|
|
||||||
where TDestination : class
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DynamicQuery<TSource, TDestination, TParams> : DynamicQuery, IDynamicQuery<TSource, TDestination, TParams>
|
|
||||||
where TSource : class
|
|
||||||
where TDestination : class
|
|
||||||
where TParams : class
|
|
||||||
{
|
|
||||||
public TParams Params { get; set; }
|
|
||||||
|
|
||||||
public TParams GetParams()
|
|
||||||
{
|
|
||||||
return Params;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DynamicQuery : IDynamicQuery
|
|
||||||
{
|
|
||||||
public int? Page { get; set; }
|
|
||||||
public int? PageSize { get; set; }
|
|
||||||
public List<Sort> Sorts { get; set; }
|
|
||||||
public List<DynamicQueryAggregate> Aggregates { get; set; }
|
|
||||||
public List<Group> Groups { get; set; }
|
|
||||||
public List<DynamicQueryFilter> Filters { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public List<IAggregate> GetAggregates()
|
|
||||||
{
|
|
||||||
return Aggregates?.Select(t => t.ToAggregate())?.ToList();//.AsEnumerable<IAggregate>()?.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<IFilter> GetFilters()
|
|
||||||
{
|
|
||||||
return Filters?.Select(t => t.ToFilter())?.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<IGroup> GetGroups()
|
|
||||||
{
|
|
||||||
return this.Groups?.AsEnumerable<IGroup>()?.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? GetPage()
|
|
||||||
{
|
|
||||||
return this.Page;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? GetPageSize()
|
|
||||||
{
|
|
||||||
return this.PageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ISort> GetSorts()
|
|
||||||
{
|
|
||||||
return this.Sorts?.AsEnumerable<ISort>()?.ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using PoweredSoft.DynamicQuery;
|
|
||||||
using PoweredSoft.DynamicQuery.Core;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore
|
|
||||||
{
|
|
||||||
public class DynamicQueryAggregate
|
|
||||||
{
|
|
||||||
public string Path { get; set; }
|
|
||||||
public string Type { get; set; }
|
|
||||||
|
|
||||||
public IAggregate ToAggregate()
|
|
||||||
{
|
|
||||||
return new Aggregate
|
|
||||||
{
|
|
||||||
Type = Enum.Parse<AggregateType>(Type),
|
|
||||||
Path = Path
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using PoweredSoft.DynamicQuery;
|
|
||||||
using PoweredSoft.DynamicQuery.Core;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore
|
|
||||||
{
|
|
||||||
public class DynamicQueryFilter
|
|
||||||
{
|
|
||||||
public List<DynamicQueryFilter> Filters { get; set; }
|
|
||||||
public bool? And { get; set; }
|
|
||||||
public string Type { get; set; }
|
|
||||||
public bool? Not { get; set; }
|
|
||||||
public string Path { get; set; }
|
|
||||||
public object Value { get; set; }
|
|
||||||
|
|
||||||
[FromQuery(Name ="value")]
|
|
||||||
public string QueryValue
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? CaseInsensitive { get; set; }
|
|
||||||
|
|
||||||
public IFilter ToFilter()
|
|
||||||
{
|
|
||||||
var type = Enum.Parse<FilterType>(Type);
|
|
||||||
if (type == FilterType.Composite)
|
|
||||||
{
|
|
||||||
var compositeFilter = new CompositeFilter
|
|
||||||
{
|
|
||||||
And = And,
|
|
||||||
Type = FilterType.Composite,
|
|
||||||
Filters = Filters?.Select(t => t.ToFilter())?.ToList() ?? new List<IFilter>()
|
|
||||||
};
|
|
||||||
return compositeFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
object value = Value;
|
|
||||||
if (Value is JsonElement jsonElement)
|
|
||||||
{
|
|
||||||
switch (jsonElement.ValueKind)
|
|
||||||
{
|
|
||||||
case JsonValueKind.String:
|
|
||||||
value = jsonElement.ToString();
|
|
||||||
break;
|
|
||||||
case JsonValueKind.Number:
|
|
||||||
if (jsonElement.ToString().Contains('.'))
|
|
||||||
value = jsonElement.GetDecimal();
|
|
||||||
else if (jsonElement.TryGetInt64(out var convertedValue))
|
|
||||||
value = convertedValue;
|
|
||||||
break;
|
|
||||||
case JsonValueKind.True:
|
|
||||||
value = true;
|
|
||||||
break;
|
|
||||||
case JsonValueKind.False:
|
|
||||||
value = false;
|
|
||||||
break;
|
|
||||||
// TODO: Array support
|
|
||||||
default:
|
|
||||||
value = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var simpleFilter = new SimpleFilter
|
|
||||||
{
|
|
||||||
And = And,
|
|
||||||
Type = type,
|
|
||||||
Not = Not,
|
|
||||||
Path = Path,
|
|
||||||
Value = value,
|
|
||||||
CaseInsensitive = CaseInsensitive,
|
|
||||||
};
|
|
||||||
return simpleFilter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using PoweredSoft.CQRS.Abstractions;
|
|
||||||
using PoweredSoft.CQRS.AspNetCore.Mvc;
|
|
||||||
using PoweredSoft.CQRS.DynamicQuery.Abstractions;
|
|
||||||
using PoweredSoft.DynamicQuery.Core;
|
|
||||||
|
|
||||||
namespace PoweredSoft.CQRS.DynamicQuery.AspNetCore.Mvc
|
|
||||||
{
|
|
||||||
[ApiController, Route("api/query/[controller]")]
|
|
||||||
public class DynamicQueryController<TSource, TDestination> : Controller
|
|
||||||
where TSource : class
|
|
||||||
where TDestination : class
|
|
||||||
{
|
|
||||||
[HttpPost, QueryControllerAuthorization]
|
|
||||||
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(
|
|
||||||
[FromBody] DynamicQuery<TSource, TDestination> query,
|
|
||||||
[FromServices]IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>> queryHandler
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet, QueryControllerAuthorization]
|
|
||||||
public async Task<IQueryExecutionResult<TDestination>> HandleGetAsync(
|
|
||||||
[FromQuery] DynamicQuery<TSource, TDestination> query,
|
|
||||||
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination>, IQueryExecutionResult<TDestination>> queryHandler
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ApiController, Route("api/query/[controller]")]
|
|
||||||
public class DynamicQueryController<TSource, TDestination, TParams> : Controller
|
|
||||||
where TSource : class
|
|
||||||
where TDestination : class
|
|
||||||
where TParams : class
|
|
||||||
{
|
|
||||||
[HttpPost, QueryControllerAuthorization]
|
|
||||||
public async Task<IQueryExecutionResult<TDestination>> HandleAsync(
|
|
||||||
[FromBody] DynamicQuery<TSource, TDestination, TParams> query,
|
|
||||||
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>> queryHandler
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet, QueryControllerAuthorization]
|
|
||||||
public async Task<IQueryExecutionResult<TDestination>> HandleGetAsync(
|
|
||||||
[FromQuery] DynamicQuery<TSource, TDestination, TParams> query,
|
|
||||||
[FromServices] IQueryHandler<IDynamicQuery<TSource, TDestination, TParams>, IQueryExecutionResult<TDestination>> queryHandler
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var result = await queryHandler.HandleAsync(query, HttpContext.RequestAborted);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user