fix injectionses
This commit is contained in:
parent
b93115d787
commit
cd078ca643
11 changed files with 119 additions and 55 deletions
|
@ -2,6 +2,7 @@ using Femto.Api.Controllers.Posts.Dto;
|
||||||
using Femto.Common;
|
using Femto.Common;
|
||||||
using Femto.Modules.Blog.Application;
|
using Femto.Modules.Blog.Application;
|
||||||
using Femto.Modules.Blog.Application.Commands.CreatePost;
|
using Femto.Modules.Blog.Application.Commands.CreatePost;
|
||||||
|
using Femto.Modules.Blog.Application.Commands.DeletePost;
|
||||||
using Femto.Modules.Blog.Application.Queries.GetPosts;
|
using Femto.Modules.Blog.Application.Queries.GetPosts;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
@ -18,7 +19,7 @@ public class PostsController(IBlogModule blogModule, ICurrentUserContext current
|
||||||
CancellationToken cancellationToken
|
CancellationToken cancellationToken
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var res = await blogModule.PostQuery(
|
var res = await blogModule.Query(
|
||||||
new GetPostsQuery(currentUserContext.CurrentUser?.Id)
|
new GetPostsQuery(currentUserContext.CurrentUser?.Id)
|
||||||
{
|
{
|
||||||
From = searchParams.From,
|
From = searchParams.From,
|
||||||
|
@ -48,7 +49,7 @@ public class PostsController(IBlogModule blogModule, ICurrentUserContext current
|
||||||
CancellationToken cancellationToken
|
CancellationToken cancellationToken
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var guid = await blogModule.PostCommand(
|
var guid = await blogModule.Command(
|
||||||
new CreatePostCommand(
|
new CreatePostCommand(
|
||||||
req.AuthorId,
|
req.AuthorId,
|
||||||
req.Content,
|
req.Content,
|
||||||
|
@ -70,4 +71,11 @@ public class PostsController(IBlogModule blogModule, ICurrentUserContext current
|
||||||
|
|
||||||
return new CreatePostResponse(guid);
|
return new CreatePostResponse(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{postId}")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task DeletePost(Guid postId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await blogModule.Command(new DeletePostCommand(postId, currentUserContext.CurrentUser.Id), cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,23 @@ namespace Femto.Api;
|
||||||
|
|
||||||
internal class CurrentUserContext : ICurrentUserContext
|
internal class CurrentUserContext : ICurrentUserContext
|
||||||
{
|
{
|
||||||
public CurrentUser? CurrentUser { get; set; }
|
private CurrentUser? _currentUser;
|
||||||
|
|
||||||
|
public CurrentUser? tryGetUserCurrentUser() => this._currentUser;
|
||||||
|
|
||||||
|
public bool HasUser => this._currentUser is not null;
|
||||||
|
|
||||||
|
public CurrentUser CurrentUser
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_currentUser is null)
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"don't access current user if not authenticated"
|
||||||
|
);
|
||||||
|
|
||||||
|
return _currentUser;
|
||||||
|
}
|
||||||
|
set => _currentUser = value;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -21,11 +21,16 @@ var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
|
|
||||||
builder.Logging.ClearProviders();
|
var loggerFactory = LoggerFactory.Create(b =>
|
||||||
builder.Logging.AddConsole();
|
{
|
||||||
builder.Logging.AddDebug();
|
b.SetMinimumLevel(LogLevel.Information)
|
||||||
builder.Logging.SetMinimumLevel(LogLevel.Information);
|
.AddConfiguration(builder.Configuration.GetSection("Logging"))
|
||||||
|
.AddConsole()
|
||||||
|
.AddDebug();
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddSingleton(loggerFactory);
|
||||||
|
builder.Services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
|
||||||
|
|
||||||
var connectionString = builder.Configuration.GetConnectionString("Database");
|
var connectionString = builder.Configuration.GetConnectionString("Database");
|
||||||
if (connectionString is null)
|
if (connectionString is null)
|
||||||
|
@ -39,9 +44,9 @@ if (blobStorageRoot is null)
|
||||||
var eventBus = new EventBus(Channel.CreateUnbounded<IEvent>());
|
var eventBus = new EventBus(Channel.CreateUnbounded<IEvent>());
|
||||||
builder.Services.AddHostedService(_ => eventBus);
|
builder.Services.AddHostedService(_ => eventBus);
|
||||||
|
|
||||||
builder.Services.InitializeBlogModule(connectionString, eventBus);
|
builder.Services.InitializeBlogModule(connectionString, eventBus, loggerFactory);
|
||||||
builder.Services.InitializeMediaModule(connectionString, blobStorageRoot);
|
builder.Services.InitializeMediaModule(connectionString, blobStorageRoot);
|
||||||
builder.Services.InitializeAuthenticationModule(connectionString, eventBus);
|
builder.Services.InitializeAuthenticationModule(connectionString, eventBus, loggerFactory);
|
||||||
|
|
||||||
builder.Services.AddScoped<CurrentUserContext, CurrentUserContext>();
|
builder.Services.AddScoped<CurrentUserContext, CurrentUserContext>();
|
||||||
builder.Services.AddScoped<ICurrentUserContext>(s => s.GetRequiredService<CurrentUserContext>());
|
builder.Services.AddScoped<ICurrentUserContext>(s => s.GetRequiredService<CurrentUserContext>());
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Femto": "Debug",
|
||||||
|
"Microsoft.AspNetCore": "Warning",
|
||||||
|
"Microsoft.EntityFrameworkCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
|
|
|
@ -2,7 +2,10 @@ namespace Femto.Common;
|
||||||
|
|
||||||
public interface ICurrentUserContext
|
public interface ICurrentUserContext
|
||||||
{
|
{
|
||||||
CurrentUser? CurrentUser { get; }
|
CurrentUser? tryGetUserCurrentUser();
|
||||||
|
|
||||||
|
bool HasUser { get; }
|
||||||
|
CurrentUser CurrentUser { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public record CurrentUser(Guid Id, string Username, string SessionId);
|
public record CurrentUser(Guid Id, string Username, string SessionId);
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Femto.Modules.Auth.Application;
|
namespace Femto.Common;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// We use this to bind a scope to the request scope in the composition root
|
/// We use this to bind a scope to the request scope in the composition root
|
||||||
/// Any scoped services provided by this subcontainer should be accessed via a ScopeBinding injected in the host
|
/// Any scoped services provided by this subcontainer should be accessed via a ScopeBinding injected in the host
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="scope"></param>
|
/// <param name="scope"></param>
|
||||||
public class ScopeBinding(IServiceScope scope) : IDisposable
|
public class ScopeBinding<T>(IServiceScope scope) : IDisposable
|
||||||
|
where T : notnull
|
||||||
{
|
{
|
||||||
public T GetService<T>() where T : notnull => scope.ServiceProvider.GetRequiredService<T>();
|
public T GetService() {
|
||||||
|
return scope.ServiceProvider.GetRequiredService<T>();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose() => scope.Dispose();
|
public void Dispose() {
|
||||||
|
scope.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Femto.Common;
|
||||||
using Femto.Common.Infrastructure;
|
using Femto.Common.Infrastructure;
|
||||||
using Femto.Common.Infrastructure.DbConnection;
|
using Femto.Common.Infrastructure.DbConnection;
|
||||||
using Femto.Common.Infrastructure.Outbox;
|
using Femto.Common.Infrastructure.Outbox;
|
||||||
|
@ -15,23 +16,36 @@ namespace Femto.Modules.Auth.Application;
|
||||||
|
|
||||||
public static class AuthStartup
|
public static class AuthStartup
|
||||||
{
|
{
|
||||||
public static void InitializeAuthenticationModule(this IServiceCollection rootContainer,
|
public static void InitializeAuthenticationModule(
|
||||||
string connectionString, IEventBus eventBus)
|
this IServiceCollection rootContainer,
|
||||||
|
string connectionString,
|
||||||
|
IEventBus eventBus,
|
||||||
|
ILoggerFactory loggerFactory
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var hostBuilder = Host.CreateDefaultBuilder();
|
var hostBuilder = Host.CreateDefaultBuilder();
|
||||||
hostBuilder.ConfigureServices(services => ConfigureServices(services, connectionString, eventBus));
|
hostBuilder.ConfigureServices(services =>
|
||||||
|
ConfigureServices(services, connectionString, eventBus, loggerFactory)
|
||||||
|
);
|
||||||
var host = hostBuilder.Build();
|
var host = hostBuilder.Build();
|
||||||
|
|
||||||
rootContainer.AddScoped(_ => new ScopeBinding(host.Services.CreateScope()));
|
rootContainer.AddScoped(_ => new ScopeBinding<IAuthModule>(host.Services.CreateScope()));
|
||||||
|
rootContainer.AddScoped(services =>
|
||||||
rootContainer.AddScoped<IAuthModule>(services =>
|
services.GetRequiredService<ScopeBinding<IAuthModule>>().GetService()
|
||||||
services.GetRequiredService<ScopeBinding>().GetService<IAuthModule>());
|
);
|
||||||
|
|
||||||
rootContainer.AddHostedService(services => new AuthApplication(host));
|
rootContainer.AddHostedService(services => new AuthApplication(host));
|
||||||
eventBus.Subscribe((evt, cancellationToken) => EventSubscriber(evt, host.Services, cancellationToken));
|
eventBus.Subscribe(
|
||||||
|
(evt, cancellationToken) => EventSubscriber(evt, host.Services, cancellationToken)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConfigureServices(IServiceCollection services, string connectionString, IEventPublisher publisher)
|
private static void ConfigureServices(
|
||||||
|
IServiceCollection services,
|
||||||
|
string connectionString,
|
||||||
|
IEventPublisher publisher,
|
||||||
|
ILoggerFactory loggerFactory
|
||||||
|
)
|
||||||
{
|
{
|
||||||
services.AddTransient<IDbConnectionFactory>(_ => new DbConnectionFactory(connectionString));
|
services.AddTransient<IDbConnectionFactory>(_ => new DbConnectionFactory(connectionString));
|
||||||
|
|
||||||
|
@ -39,13 +53,15 @@ public static class AuthStartup
|
||||||
{
|
{
|
||||||
builder.UseNpgsql(connectionString);
|
builder.UseNpgsql(connectionString);
|
||||||
builder.UseSnakeCaseNamingConvention();
|
builder.UseSnakeCaseNamingConvention();
|
||||||
var loggerFactory = LoggerFactory.Create(b => { });
|
|
||||||
builder.UseLoggerFactory(loggerFactory);
|
builder.UseLoggerFactory(loggerFactory);
|
||||||
// #if DEBUG
|
// #if DEBUG
|
||||||
// builder.EnableSensitiveDataLogging();
|
// builder.EnableSensitiveDataLogging();
|
||||||
// #endif
|
// #endif
|
||||||
});
|
});
|
||||||
|
|
||||||
|
services.AddSingleton(loggerFactory);
|
||||||
|
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
|
||||||
|
|
||||||
services.AddQuartzHostedService(options =>
|
services.AddQuartzHostedService(options =>
|
||||||
{
|
{
|
||||||
options.WaitForJobsToComplete = true;
|
options.WaitForJobsToComplete = true;
|
||||||
|
|
|
@ -1,37 +1,29 @@
|
||||||
using Femto.Common.Domain;
|
using Femto.Common.Domain;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
namespace Femto.Modules.Blog.Application;
|
namespace Femto.Modules.Blog.Application;
|
||||||
|
|
||||||
internal class BlogModule(IHost host) : IBlogModule
|
internal class BlogModule(IMediator mediator) : IBlogModule
|
||||||
{
|
{
|
||||||
public async Task PostCommand(ICommand command, CancellationToken cancellationToken = default)
|
public async Task Command(ICommand command, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
using var scope = host.Services.CreateScope();
|
|
||||||
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
|
|
||||||
await mediator.Send(command, cancellationToken);
|
await mediator.Send(command, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TResponse> PostCommand<TResponse>(
|
public async Task<TResponse> Command<TResponse>(
|
||||||
ICommand<TResponse> command,
|
ICommand<TResponse> command,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using var scope = host.Services.CreateScope();
|
|
||||||
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
|
|
||||||
var response = await mediator.Send(command, cancellationToken);
|
var response = await mediator.Send(command, cancellationToken);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TResponse> PostQuery<TResponse>(
|
public async Task<TResponse> Query<TResponse>(
|
||||||
IQuery<TResponse> query,
|
IQuery<TResponse> query,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using var scope = host.Services.CreateScope();
|
|
||||||
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
|
|
||||||
var response = await mediator.Send(query, cancellationToken);
|
var response = await mediator.Send(query, cancellationToken);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using Femto.Common;
|
||||||
using Femto.Common.Infrastructure;
|
using Femto.Common.Infrastructure;
|
||||||
using Femto.Common.Infrastructure.DbConnection;
|
using Femto.Common.Infrastructure.DbConnection;
|
||||||
using Femto.Common.Infrastructure.Outbox;
|
using Femto.Common.Infrastructure.Outbox;
|
||||||
|
@ -20,20 +21,24 @@ public static class BlogStartup
|
||||||
public static void InitializeBlogModule(
|
public static void InitializeBlogModule(
|
||||||
this IServiceCollection rootContainer,
|
this IServiceCollection rootContainer,
|
||||||
string connectionString,
|
string connectionString,
|
||||||
IEventBus bus
|
IEventBus bus,
|
||||||
|
ILoggerFactory loggerFactory
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var hostBuilder = Host.CreateDefaultBuilder();
|
var hostBuilder = Host.CreateDefaultBuilder();
|
||||||
|
|
||||||
hostBuilder.ConfigureServices(services =>
|
hostBuilder.ConfigureServices(services =>
|
||||||
ConfigureServices(services, connectionString, bus)
|
ConfigureServices(services, connectionString, bus, loggerFactory)
|
||||||
);
|
);
|
||||||
|
|
||||||
var host = hostBuilder.Build();
|
var host = hostBuilder.Build();
|
||||||
|
|
||||||
rootContainer.AddHostedService(services => new BlogApplication(host));
|
rootContainer.AddHostedService(_ => new BlogApplication(host));
|
||||||
|
|
||||||
rootContainer.AddScoped<IBlogModule>(_ => new BlogModule(host));
|
rootContainer.AddScoped(_ => new ScopeBinding<IBlogModule>(host.Services.CreateScope()));
|
||||||
|
rootContainer.AddScoped(services =>
|
||||||
|
services.GetRequiredService<ScopeBinding<IBlogModule>>().GetService()
|
||||||
|
);
|
||||||
|
|
||||||
bus.Subscribe(
|
bus.Subscribe(
|
||||||
(evt, cancellationToken) => EventSubscriber(evt, host.Services, cancellationToken)
|
(evt, cancellationToken) => EventSubscriber(evt, host.Services, cancellationToken)
|
||||||
|
@ -43,7 +48,8 @@ public static class BlogStartup
|
||||||
private static void ConfigureServices(
|
private static void ConfigureServices(
|
||||||
this IServiceCollection services,
|
this IServiceCollection services,
|
||||||
string connectionString,
|
string connectionString,
|
||||||
IEventPublisher publisher
|
IEventPublisher publisher,
|
||||||
|
ILoggerFactory loggerFactory
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
services.AddTransient<IDbConnectionFactory>(_ => new DbConnectionFactory(connectionString));
|
services.AddTransient<IDbConnectionFactory>(_ => new DbConnectionFactory(connectionString));
|
||||||
|
@ -58,10 +64,12 @@ public static class BlogStartup
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
builder.UseSnakeCaseNamingConvention();
|
builder.UseSnakeCaseNamingConvention();
|
||||||
var loggerFactory = LoggerFactory.Create(b => { });
|
|
||||||
builder.UseLoggerFactory(loggerFactory);
|
builder.UseLoggerFactory(loggerFactory);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
services.AddSingleton(loggerFactory);
|
||||||
|
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
|
||||||
|
|
||||||
services.AddOutbox<BlogContext, OutboxMessageHandler>();
|
services.AddOutbox<BlogContext, OutboxMessageHandler>();
|
||||||
|
|
||||||
services.AddMediatR(c =>
|
services.AddMediatR(c =>
|
||||||
|
@ -71,6 +79,8 @@ public static class BlogStartup
|
||||||
|
|
||||||
services.ConfigureDomainServices<BlogContext>();
|
services.ConfigureDomainServices<BlogContext>();
|
||||||
|
|
||||||
|
services.AddScoped<IBlogModule, BlogModule>();
|
||||||
|
|
||||||
services.AddSingleton(publisher);
|
services.AddSingleton(publisher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
using Femto.Common.Domain;
|
||||||
|
|
||||||
|
namespace Femto.Modules.Blog.Application.Commands.DeletePost;
|
||||||
|
|
||||||
|
public record DeletePostCommand(Guid PostId, Guid InitiatingUserId) : ICommand;
|
|
@ -4,14 +4,14 @@ namespace Femto.Modules.Blog.Application;
|
||||||
|
|
||||||
public interface IBlogModule
|
public interface IBlogModule
|
||||||
{
|
{
|
||||||
Task PostCommand(ICommand command, CancellationToken cancellationToken = default);
|
Task Command(ICommand command, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task<TResponse> PostCommand<TResponse>(
|
Task<TResponse> Command<TResponse>(
|
||||||
ICommand<TResponse> command,
|
ICommand<TResponse> command,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
);
|
);
|
||||||
|
|
||||||
Task<TResponse> PostQuery<TResponse>(
|
Task<TResponse> Query<TResponse>(
|
||||||
IQuery<TResponse> query,
|
IQuery<TResponse> query,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue