mirror of
https://github.com/alex289/CleanArchitecture.git
synced 2025-06-30 02:31:08 +00:00
Add first endpoint
This commit is contained in:
parent
d884b03336
commit
b58c74c666
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@ obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
.idea
|
||||
.idea
|
||||
.vs
|
||||
|
@ -22,6 +22,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
|
||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Application.Interfaces;
|
||||
using CleanArchitecture.Domain.Notifications;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -9,8 +11,13 @@ namespace CleanArchitecture.Api.Controllers;
|
||||
[Route("[controller]")]
|
||||
public class UserController : ApiController
|
||||
{
|
||||
public UserController(NotificationHandler<DomainNotification> notifications) : base(notifications)
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public UserController(
|
||||
INotificationHandler<DomainNotification> notifications,
|
||||
IUserService userService) : base(notifications)
|
||||
{
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@ -20,9 +27,10 @@ public class UserController : ApiController
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public string GetUserByIdAsync([FromRoute] Guid id)
|
||||
public async Task<IActionResult> GetUserByIdAsync([FromRoute] Guid id)
|
||||
{
|
||||
return "test";
|
||||
var user = await _userService.GetUserByUserIdAsync(id);
|
||||
return Response(user);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
|
@ -1,9 +1,10 @@
|
||||
using CleanArchitecture.Application.Extensions;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using CleanArchitecture.Infrastructure.Extensions;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -11,13 +12,19 @@ builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
{
|
||||
options.UseLazyLoadingProxies();
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"),
|
||||
b => b.MigrationsAssembly("netgo.centralhub.TenantService.Infrastructure"));
|
||||
b => b.MigrationsAssembly("CleanArchitecture.Infrastructure"));
|
||||
});
|
||||
|
||||
builder.Services.AddInfrastructure();
|
||||
builder.Services.AddQueryHandlers();
|
||||
builder.Services.AddServices();
|
||||
|
||||
builder.Services.AddMediatR(cfg =>
|
||||
{
|
||||
cfg.RegisterServicesFromAssemblies(typeof(Program).Assembly);
|
||||
@ -25,12 +32,8 @@ builder.Services.AddMediatR(cfg =>
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
@ -38,6 +41,14 @@ app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
using (IServiceScope scope = app.Services.CreateScope())
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
ApplicationDbContext appDbContext = services.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
appDbContext.EnsureMigrationsApplied();
|
||||
}
|
||||
|
||||
app.Run();
|
||||
|
||||
// Needed for integration tests webapplication factory
|
||||
|
@ -9,20 +9,10 @@
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"CleanArchitecture.Api": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:5201",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchBrowser": false,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7260;http://localhost:5201",
|
||||
"environmentVariables": {
|
||||
|
@ -5,5 +5,8 @@
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=localhost;Database=clean-architecture;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Queries" />
|
||||
<Folder Include="ViewModels" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MediatR" Version="12.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,5 +1,8 @@
|
||||
using CleanArchitecture.Application.Interfaces;
|
||||
using CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||
using CleanArchitecture.Application.Services;
|
||||
using CleanArchitecture.Application.ViewModels;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CleanArchitecture.Application.Extensions;
|
||||
@ -15,7 +18,7 @@ public static class ServiceCollectionExtension
|
||||
|
||||
public static IServiceCollection AddQueryHandlers(this IServiceCollection services)
|
||||
{
|
||||
// services.AddScoped<IQueryHandler<GetUserByIdQuery, User>, GetUserByIdQueryHandler>();
|
||||
services.AddScoped<IRequestHandler<GetUserByIdQuery, UserViewModel?>, GetUserByIdQueryHandler>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
using CleanArchitecture.Application.ViewModels;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
|
||||
namespace CleanArchitecture.Application.Interfaces;
|
||||
|
||||
public interface IUserService
|
||||
{
|
||||
|
||||
public Task<UserViewModel?> GetUserByUserIdAsync(Guid userId);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
using System;
|
||||
using CleanArchitecture.Application.ViewModels;
|
||||
using MediatR;
|
||||
|
||||
namespace CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||
|
||||
public sealed record GetUserByIdQuery(Guid UserId) : IRequest<UserViewModel?>;
|
@ -0,0 +1,44 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Application.ViewModels;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
using CleanArchitecture.Domain.Interfaces;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
using CleanArchitecture.Domain.Notifications;
|
||||
using MediatR;
|
||||
|
||||
namespace CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||
|
||||
public sealed class GetUserByIdQueryHandler :
|
||||
IRequestHandler<GetUserByIdQuery, UserViewModel?>
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IMediatorHandler _bus;
|
||||
|
||||
public GetUserByIdQueryHandler(IUserRepository userRepository, IMediatorHandler bus)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
_bus = bus;
|
||||
}
|
||||
|
||||
public async Task<UserViewModel?> Handle(GetUserByIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = _userRepository
|
||||
.GetAllNoTracking()
|
||||
.Where(x => x.Id == request.UserId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
await _bus.RaiseEventAsync(
|
||||
new DomainNotification(
|
||||
nameof(GetUserByIdQuery),
|
||||
$"User with id {request.UserId} could not be found",
|
||||
ErrorCodes.ObjectNotFound));
|
||||
return null;
|
||||
}
|
||||
|
||||
return UserViewModel.FromUser(user);
|
||||
}
|
||||
}
|
@ -1,7 +1,23 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Application.Interfaces;
|
||||
using CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||
using CleanArchitecture.Application.ViewModels;
|
||||
using CleanArchitecture.Domain.Interfaces;
|
||||
|
||||
namespace CleanArchitecture.Application.Services;
|
||||
|
||||
public sealed class UserService : IUserService
|
||||
{
|
||||
private readonly IMediatorHandler _bus;
|
||||
|
||||
public UserService(IMediatorHandler bus)
|
||||
{
|
||||
_bus = bus;
|
||||
}
|
||||
|
||||
public async Task<UserViewModel?> GetUserByUserIdAsync(Guid userId)
|
||||
{
|
||||
return await _bus.QueryAsync(new GetUserByIdQuery(userId));
|
||||
}
|
||||
}
|
23
CleanArchitecture.Application/viewmodels/UserViewModel.cs
Normal file
23
CleanArchitecture.Application/viewmodels/UserViewModel.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using CleanArchitecture.Domain.Entities;
|
||||
|
||||
namespace CleanArchitecture.Application.ViewModels;
|
||||
|
||||
public sealed class UserViewModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string GivenName { get; set; } = string.Empty;
|
||||
public string Surname { get; set; } = string.Empty;
|
||||
|
||||
public static UserViewModel FromUser(User user)
|
||||
{
|
||||
return new UserViewModel
|
||||
{
|
||||
Id = user.Id,
|
||||
Email = user.Email,
|
||||
GivenName = user.GivenName,
|
||||
Surname = user.Surname
|
||||
};
|
||||
}
|
||||
}
|
@ -12,6 +12,12 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MediatR" Version="12.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -0,0 +1,21 @@
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace CleanArchitecture.Infrastructure.Extensions;
|
||||
|
||||
public static class DbContextExtension
|
||||
{
|
||||
public static void EnsureMigrationsApplied(this DbContext context)
|
||||
{
|
||||
var applied = context.GetService<IHistoryRepository>().GetAppliedMigrations().Select(m => m.MigrationId);
|
||||
|
||||
var total = context.GetService<IMigrationsAssembly>().Migrations.Select(m => m.Key);
|
||||
|
||||
if (total.Except(applied).Any())
|
||||
{
|
||||
context.Database.Migrate();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
using CleanArchitecture.Domain.Interfaces;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
using CleanArchitecture.Domain.Notifications;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using CleanArchitecture.Infrastructure.Repositories;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CleanArchitecture.Infrastructure.Extensions;
|
||||
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
||||
{
|
||||
// Core Infra
|
||||
services.AddScoped<IUnitOfWork, UnitOfWork<ApplicationDbContext>>();
|
||||
services.AddScoped<INotificationHandler<DomainNotification>, DomainNotificationHandler>();
|
||||
services.AddScoped<IMediatorHandler, InMemoryBus>();
|
||||
|
||||
// Repositories
|
||||
services.AddScoped<IUserRepository, UserRepository>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
62
CleanArchitecture.Infrastructure/Migrations/20230306172301_InitialMigration.Designer.cs
generated
Normal file
62
CleanArchitecture.Infrastructure/Migrations/20230306172301_InitialMigration.Designer.cs
generated
Normal file
@ -0,0 +1,62 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CleanArchitecture.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20230306172301_InitialMigration")]
|
||||
partial class InitialMigration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.3")
|
||||
.HasAnnotation("Proxies:ChangeTracking", false)
|
||||
.HasAnnotation("Proxies:CheckEquality", false)
|
||||
.HasAnnotation("Proxies:LazyLoading", true)
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CleanArchitecture.Domain.Entities.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<bool>("Deleted")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasMaxLength(320)
|
||||
.HasColumnType("nvarchar(320)");
|
||||
|
||||
b.Property<string>("GivenName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Surname")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CleanArchitecture.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialMigration : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
Email = table.Column<string>(type: "nvarchar(320)", maxLength: 320, nullable: false),
|
||||
GivenName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||
Surname = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||
Deleted = table.Column<bool>(type: "bit", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CleanArchitecture.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.3")
|
||||
.HasAnnotation("Proxies:ChangeTracking", false)
|
||||
.HasAnnotation("Proxies:CheckEquality", false)
|
||||
.HasAnnotation("Proxies:LazyLoading", true)
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CleanArchitecture.Domain.Entities.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<bool>("Deleted")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasMaxLength(320)
|
||||
.HasColumnType("nvarchar(320)");
|
||||
|
||||
b.Property<string>("GivenName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Surname")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user