mirror of
https://github.com/alex289/CleanArchitecture.git
synced 2025-06-30 02:31:08 +00:00
Add gRPC and fix some warnings
This commit is contained in:
parent
2c8bf95254
commit
f645b2cc8f
@ -24,6 +24,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
|
||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
||||
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj" />
|
||||
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -56,7 +56,7 @@ public class ApiController : ControllerBase
|
||||
return GetErrorStatusCode();
|
||||
}
|
||||
|
||||
protected HttpStatusCode GetErrorStatusCode()
|
||||
private HttpStatusCode GetErrorStatusCode()
|
||||
{
|
||||
if (_notifications.GetNotifications().Any(n => n.Code == ErrorCodes.ObjectNotFound))
|
||||
{
|
||||
|
@ -29,9 +29,11 @@ public class UserController : ApiController
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetUserByIdAsync([FromRoute] Guid id)
|
||||
public async Task<IActionResult> GetUserByIdAsync(
|
||||
[FromRoute] Guid id,
|
||||
[FromQuery] bool isDeleted = false)
|
||||
{
|
||||
var user = await _userService.GetUserByUserIdAsync(id);
|
||||
var user = await _userService.GetUserByUserIdAsync(id, isDeleted);
|
||||
return Response(user);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using CleanArchitecture.Application.Extensions;
|
||||
using CleanArchitecture.Domain.Extensions;
|
||||
using CleanArchitecture.gRPC;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using CleanArchitecture.Infrastructure.Extensions;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
@ -10,6 +11,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddGrpc();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
@ -43,6 +45,7 @@ app.UseHttpsRedirection();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
app.MapGrpcService<UsersApiImplementation>();
|
||||
|
||||
using (IServiceScope scope = app.Services.CreateScope())
|
||||
{
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using CleanArchitecture.Application.Queries.Users.GetAll;
|
||||
using CleanArchitecture.Domain.Entities;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
@ -8,7 +10,7 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
|
||||
|
||||
public sealed class GetAllUsersTestFixture : QueryHandlerBaseFixture
|
||||
{
|
||||
public Mock<IUserRepository> UserRepository { get; }
|
||||
private Mock<IUserRepository> UserRepository { get; }
|
||||
public GetAllUsersQueryHandler Handler { get; }
|
||||
public Guid ExistingUserId { get; } = Guid.NewGuid();
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||
using CleanArchitecture.Domain.Entities;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
@ -8,7 +10,7 @@ namespace CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
|
||||
|
||||
public sealed class GetUserByIdTestFixture : QueryHandlerBaseFixture
|
||||
{
|
||||
public Mock<IUserRepository> UserRepository { get; }
|
||||
private Mock<IUserRepository> UserRepository { get; }
|
||||
public GetUserByIdQueryHandler Handler { get; }
|
||||
public Guid ExistingUserId { get; } = Guid.NewGuid();
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
@ -18,7 +20,7 @@ public sealed class GetAllUsersQueryHandlerTests
|
||||
default);
|
||||
|
||||
_fixture.VerifyNoDomainNotification();
|
||||
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result.Should().ContainSingle();
|
||||
result.FirstOrDefault()!.Id.Should().Be(_fixture.ExistingUserId);
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||
using CleanArchitecture.Application.Tests.Fixtures.Queries.Users;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
@ -16,7 +18,7 @@ public sealed class GetUserByIdQueryHandlerTests
|
||||
_fixture.SetupUserAsync();
|
||||
|
||||
var result = await _fixture.Handler.Handle(
|
||||
new(_fixture.ExistingUserId),
|
||||
new(_fixture.ExistingUserId, false),
|
||||
default);
|
||||
|
||||
_fixture.VerifyNoDomainNotification();
|
||||
@ -30,7 +32,7 @@ public sealed class GetUserByIdQueryHandlerTests
|
||||
{
|
||||
_fixture.SetupUserAsync();
|
||||
|
||||
var request = new GetUserByIdQuery(Guid.NewGuid());
|
||||
var request = new GetUserByIdQuery(Guid.NewGuid(), false);
|
||||
var result = await _fixture.Handler.Handle(
|
||||
request,
|
||||
default);
|
||||
|
@ -3,7 +3,6 @@ using CleanArchitecture.Application.Interfaces;
|
||||
using CleanArchitecture.Application.Queries.Users.GetAll;
|
||||
using CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||
using CleanArchitecture.Application.Services;
|
||||
using CleanArchitecture.Application.ViewModels;
|
||||
using CleanArchitecture.Application.ViewModels.Users;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
@ -7,7 +7,7 @@ namespace CleanArchitecture.Application.Interfaces;
|
||||
|
||||
public interface IUserService
|
||||
{
|
||||
public Task<UserViewModel?> GetUserByUserIdAsync(Guid userId);
|
||||
public Task<UserViewModel?> GetUserByUserIdAsync(Guid userId, bool isDeleted);
|
||||
public Task<IEnumerable<UserViewModel>> GetAllUsersAsync();
|
||||
public Task<Guid> CreateUserAsync(CreateUserViewModel user);
|
||||
public Task UpdateUserAsync(UpdateUserViewModel user);
|
||||
|
@ -1,8 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using CleanArchitecture.Application.ViewModels;
|
||||
using CleanArchitecture.Application.ViewModels.Users;
|
||||
using MediatR;
|
||||
|
||||
namespace CleanArchitecture.Application.Queries.Users.GetAll;
|
||||
|
||||
public sealed record GetAllUsersQuery() : IRequest<IEnumerable<UserViewModel>>;
|
||||
public sealed record GetAllUsersQuery : IRequest<IEnumerable<UserViewModel>>;
|
||||
|
@ -2,7 +2,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Application.ViewModels;
|
||||
using CleanArchitecture.Application.ViewModels.Users;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
using MediatR;
|
||||
|
@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using CleanArchitecture.Application.ViewModels;
|
||||
using CleanArchitecture.Application.ViewModels.Users;
|
||||
using MediatR;
|
||||
|
||||
namespace CleanArchitecture.Application.Queries.Users.GetUserById;
|
||||
|
||||
public sealed record GetUserByIdQuery(Guid UserId) : IRequest<UserViewModel?>;
|
||||
public sealed record GetUserByIdQuery(Guid UserId, bool IsDeleted) : IRequest<UserViewModel?>;
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Application.ViewModels;
|
||||
using CleanArchitecture.Application.ViewModels.Users;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
using CleanArchitecture.Domain.Interfaces;
|
||||
@ -27,7 +26,9 @@ public sealed class GetUserByIdQueryHandler :
|
||||
{
|
||||
var user = _userRepository
|
||||
.GetAllNoTracking()
|
||||
.FirstOrDefault(x => x.Id == request.UserId && !x.Deleted);
|
||||
.FirstOrDefault(x =>
|
||||
x.Id == request.UserId &&
|
||||
x.Deleted == request.IsDeleted);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
|
@ -21,9 +21,9 @@ public sealed class UserService : IUserService
|
||||
_bus = bus;
|
||||
}
|
||||
|
||||
public async Task<UserViewModel?> GetUserByUserIdAsync(Guid userId)
|
||||
public async Task<UserViewModel?> GetUserByUserIdAsync(Guid userId, bool isDeleted)
|
||||
{
|
||||
return await _bus.QueryAsync(new GetUserByIdQuery(userId));
|
||||
return await _bus.QueryAsync(new GetUserByIdQuery(userId, isDeleted));
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<UserViewModel>> GetAllUsersAsync()
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
using CleanArchitecture.Domain.Events.User;
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
using Moq;
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
using Xunit;
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
using CleanArchitecture.Domain.Events.User;
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
using Moq;
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
using Xunit;
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
using CleanArchitecture.Domain.Events.User;
|
||||
@ -31,7 +33,7 @@ public sealed class UpdateUserCommandHandlerTests
|
||||
[Fact]
|
||||
public async Task Should_Not_Update_Non_Existing_User()
|
||||
{
|
||||
var user = _fixture.SetupUser();
|
||||
_fixture.SetupUser();
|
||||
|
||||
var command = new UpdateUserCommand(
|
||||
Guid.NewGuid(),
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
using Moq;
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using CleanArchitecture.Domain.Commands.Users.UpdateUser;
|
||||
using CleanArchitecture.Domain.Errors;
|
||||
using Xunit;
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using CleanArchitecture.Domain.Interfaces;
|
||||
using CleanArchitecture.Domain.Notifications;
|
||||
@ -7,9 +8,9 @@ namespace CleanArchitecture.Domain.Tests;
|
||||
|
||||
public class CommandHandlerFixtureBase
|
||||
{
|
||||
public Mock<IMediatorHandler> Bus { get; protected set; }
|
||||
public Mock<IUnitOfWork> UnitOfWork { get; protected set; }
|
||||
public Mock<DomainNotificationHandler> NotificationHandler { get; protected set; }
|
||||
protected Mock<IMediatorHandler> Bus { get; }
|
||||
protected Mock<IUnitOfWork> UnitOfWork { get; }
|
||||
protected Mock<DomainNotificationHandler> NotificationHandler { get; }
|
||||
|
||||
protected CommandHandlerFixtureBase()
|
||||
{
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CleanArchitecture.Domain.Commands;
|
||||
using FluentAssertions;
|
||||
using FluentValidation;
|
||||
@ -8,7 +10,7 @@ public class ValidationTestBase<TCommand, TValidation>
|
||||
where TCommand : CommandBase
|
||||
where TValidation: AbstractValidator<TCommand>
|
||||
{
|
||||
protected readonly TValidation _validation;
|
||||
private readonly TValidation _validation;
|
||||
|
||||
protected ValidationTestBase(TValidation validation)
|
||||
{
|
||||
|
@ -4,7 +4,7 @@ namespace CleanArchitecture.Domain.Commands.Users.CreateUser;
|
||||
|
||||
public sealed class CreateUserCommand : CommandBase
|
||||
{
|
||||
private static readonly CreateUserCommandValidation _validation = new();
|
||||
private readonly CreateUserCommandValidation _validation = new();
|
||||
|
||||
public Guid UserId { get; }
|
||||
public string Email { get; }
|
||||
|
@ -4,7 +4,7 @@ namespace CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||
|
||||
public sealed class DeleteUserCommand : CommandBase
|
||||
{
|
||||
private static readonly DeleteUserCommandValidation _validation = new();
|
||||
private readonly DeleteUserCommandValidation _validation = new();
|
||||
|
||||
public Guid UserId { get; }
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
@ -35,7 +35,7 @@ public sealed class DomainNotificationHandlerTests
|
||||
|
||||
var domainNotification = new DomainNotification(key, value, code);
|
||||
var domainNotificationHandler = new DomainNotificationHandler();
|
||||
domainNotificationHandler.Handle(domainNotification, default);
|
||||
domainNotificationHandler.Handle(domainNotification);
|
||||
domainNotificationHandler.GetNotifications().Should().HaveCount(1);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using CleanArchitecture.Domain.Notifications;
|
||||
using System;
|
||||
using CleanArchitecture.Domain.Notifications;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Domain.Commands.Users.DeleteUser;
|
||||
using CleanArchitecture.Domain.Events.User;
|
||||
using CleanArchitecture.Domain.Notifications;
|
||||
|
@ -1,3 +1,6 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using CleanArchitecture.Infrastructure.Tests.Fixtures;
|
||||
using FluentAssertions;
|
||||
@ -37,7 +40,7 @@ public sealed class UnitOfWorkTests
|
||||
|
||||
dbContextMock
|
||||
.Setup(x => x.SaveChangesAsync(CancellationToken.None))
|
||||
.Throws(new DbUpdateException("Boom", new System.Exception("it broke")));
|
||||
.Throws(new DbUpdateException("Boom", new Exception("it broke")));
|
||||
|
||||
var unitOfWork = UnitOfWorkTestFixture.GetUnitOfWork(dbContextMock.Object, loggerMock.Object);
|
||||
|
||||
@ -59,8 +62,8 @@ public sealed class UnitOfWorkTests
|
||||
|
||||
var unitOfWork = UnitOfWorkTestFixture.GetUnitOfWork(dbContextMock.Object, loggerMock.Object);
|
||||
|
||||
Func<Task> knalltAction = async () => await unitOfWork.CommitAsync();
|
||||
Func<Task> throwsAction = async () => await unitOfWork.CommitAsync();
|
||||
|
||||
await knalltAction.Should().ThrowAsync<Exception>();
|
||||
await throwsAction.Should().ThrowAsync<Exception>();
|
||||
}
|
||||
}
|
@ -10,23 +10,23 @@ namespace CleanArchitecture.Infrastructure.Repositories;
|
||||
|
||||
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : Entity
|
||||
{
|
||||
protected readonly DbContext _dbContext;
|
||||
protected readonly DbSet<TEntity> _dbSet;
|
||||
private readonly DbContext _dbContext;
|
||||
protected readonly DbSet<TEntity> DbSet;
|
||||
|
||||
public BaseRepository(DbContext context)
|
||||
protected BaseRepository(DbContext context)
|
||||
{
|
||||
_dbContext = context;
|
||||
_dbSet = _dbContext.Set<TEntity>();
|
||||
DbSet = _dbContext.Set<TEntity>();
|
||||
}
|
||||
|
||||
public void Add(TEntity entity)
|
||||
{
|
||||
_dbSet.Add(entity);
|
||||
DbSet.Add(entity);
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable<TEntity> entities)
|
||||
{
|
||||
_dbSet.AddRange(entities);
|
||||
DbSet.AddRange(entities);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@ -37,17 +37,17 @@ public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : Enti
|
||||
|
||||
public virtual IQueryable<TEntity> GetAll()
|
||||
{
|
||||
return _dbSet;
|
||||
return DbSet;
|
||||
}
|
||||
|
||||
public virtual IQueryable<TEntity> GetAllNoTracking()
|
||||
{
|
||||
return _dbSet.AsNoTracking();
|
||||
return DbSet.AsNoTracking();
|
||||
}
|
||||
|
||||
public virtual async Task<TEntity?> GetByIdAsync(Guid id)
|
||||
{
|
||||
return await _dbSet.FindAsync(id);
|
||||
return await DbSet.FindAsync(id);
|
||||
}
|
||||
|
||||
public int SaveChanges()
|
||||
@ -65,24 +65,24 @@ public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : Enti
|
||||
|
||||
public virtual void Update(TEntity entity)
|
||||
{
|
||||
_dbSet.Update(entity);
|
||||
DbSet.Update(entity);
|
||||
}
|
||||
|
||||
public Task<bool> ExistsAsync(Guid id)
|
||||
{
|
||||
return _dbSet.AnyAsync(entity => entity.Id == id);
|
||||
return DbSet.AnyAsync(entity => entity.Id == id);
|
||||
}
|
||||
|
||||
public void Remove(TEntity entity, bool hardDelete = false)
|
||||
{
|
||||
if (hardDelete)
|
||||
{
|
||||
_dbSet.Remove(entity);
|
||||
DbSet.Remove(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.Delete();
|
||||
_dbSet.Update(entity);
|
||||
DbSet.Update(entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,6 @@ public sealed class UserRepository : BaseRepository<User>, IUserRepository
|
||||
|
||||
public async Task<User?> GetByEmailAsync(string email)
|
||||
{
|
||||
return await _dbSet.SingleOrDefaultAsync(user => user.Email == email);
|
||||
return await DbSet.SingleOrDefaultAsync(user => user.Email == email);
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CleanArchitecture.Infrastructure;
|
||||
|
||||
public class UnitOfWork<TContext> : IUnitOfWork where TContext : DbContext
|
||||
public sealed class UnitOfWork<TContext> : IUnitOfWork where TContext : DbContext
|
||||
{
|
||||
private readonly TContext _context;
|
||||
private readonly ILogger<UnitOfWork<TContext>> _logger;
|
||||
@ -34,10 +34,11 @@ public class UnitOfWork<TContext> : IUnitOfWork where TContext : DbContext
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// ReSharper disable once GCSuppressFinalizeForTypeWithoutDestructor
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
@ -1,4 +1,8 @@
|
||||
using System.Net;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Application.ViewModels.Users;
|
||||
using CleanArchitecture.IntegrationTests.Extensions;
|
||||
using CleanArchitecture.IntegrationTests.Fixtures;
|
||||
@ -8,7 +12,7 @@ using Xunit.Priority;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests.Controller;
|
||||
|
||||
[Collection("Integrationtests")]
|
||||
[Collection("IntegrationTests")]
|
||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
||||
public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
||||
{
|
||||
@ -122,7 +126,7 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
||||
|
||||
message?.Data.Should().NotBeNull();
|
||||
|
||||
var content = message!.Data!;
|
||||
var content = message!.Data!.ToList();
|
||||
|
||||
content.Should().ContainSingle();
|
||||
content.First().Id.Should().Be(_fixture.CreatedUserId);
|
||||
@ -142,7 +146,7 @@ public sealed class UserControllerTests : IClassFixture<UserTestFixture>
|
||||
|
||||
message?.Data.Should().NotBeEmpty();
|
||||
|
||||
var content = message!.Data!;
|
||||
var content = message!.Data;
|
||||
content.Should().Be(_fixture.CreatedUserId);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
using System.Data.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@ -17,7 +20,7 @@ public static class FunctionalTestsServiceCollectionExtensions
|
||||
services.AddScoped(p =>
|
||||
DbContextOptionsFactory<TContext>(
|
||||
p,
|
||||
(sp, options) => options
|
||||
(_, options) => options
|
||||
.ConfigureWarnings(b => b.Log(CoreEventId.ManyServiceProvidersCreatedWarning))
|
||||
.UseLazyLoadingProxies()
|
||||
.UseSqlite(connection)));
|
||||
@ -35,7 +38,7 @@ public static class FunctionalTestsServiceCollectionExtensions
|
||||
|
||||
builder.UseApplicationServiceProvider(applicationServiceProvider);
|
||||
|
||||
optionsAction?.Invoke(applicationServiceProvider, builder);
|
||||
optionsAction.Invoke(applicationServiceProvider, builder);
|
||||
|
||||
return builder.Options;
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
using System.Text;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Api.Models;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests.Extensions;
|
||||
|
||||
public static class HttpExensions
|
||||
public static class HttpExtensions
|
||||
{
|
||||
public static JsonSerializerOptions JsonSerializerOptions = new()
|
||||
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
};
|
||||
|
@ -1,4 +1,7 @@
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using CleanArchitecture.IntegrationTests.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
namespace CleanArchitecture.IntegrationTests.Fixtures;
|
||||
using System;
|
||||
|
||||
namespace CleanArchitecture.IntegrationTests.Fixtures;
|
||||
|
||||
public sealed class UserTestFixture : TestFixtureBase
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using System;
|
||||
using CleanArchitecture.Infrastructure.Database;
|
||||
using CleanArchitecture.Infrastructure.Extensions;
|
||||
using CleanArchitecture.IntegrationTests.Extensions;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
24
CleanArchitecture.Proto/CleanArchitecture.Proto.csproj
Normal file
24
CleanArchitecture.Proto/CleanArchitecture.Proto.csproj
Normal file
@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Users\Models.proto"/>
|
||||
<None Remove="Users\UsersApi.proto"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Users\Models.proto" GrpcServices="Both"/>
|
||||
<Protobuf Include="Users\UsersApi.proto" GrpcServices="Both"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
|
||||
<PackageReference Include="Google.Protobuf.Tools" Version="3.22.1" />
|
||||
<PackageReference Include="Grpc.AspNetCore" Version="2.51.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
11
CleanArchitecture.Proto/Users/Models.proto
Normal file
11
CleanArchitecture.Proto/Users/Models.proto
Normal file
@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "CleanArchitecture.Proto.Users";
|
||||
|
||||
message GrpcUser {
|
||||
string id = 1;
|
||||
string firstName = 3;
|
||||
string lastName = 4;
|
||||
string email = 5;
|
||||
bool isDeleted = 6;
|
||||
}
|
17
CleanArchitecture.Proto/Users/UsersApi.proto
Normal file
17
CleanArchitecture.Proto/Users/UsersApi.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "CleanArchitecture.Proto.Users";
|
||||
|
||||
import "Users/Models.proto";
|
||||
|
||||
service UsersApi {
|
||||
rpc GetByIds(GetByIdsRequest) returns (GetByIdsResult);
|
||||
}
|
||||
|
||||
message GetByIdsResult {
|
||||
repeated GrpcUser users = 1;
|
||||
}
|
||||
|
||||
message GetByIdsRequest {
|
||||
repeated string ids = 1;
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.10.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="MockQueryable.Moq" Version="7.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.18.4" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
||||
<ProjectReference Include="..\CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
36
CleanArchitecture.gRPC.Tests/Fixtures/UserTestsFixture.cs
Normal file
36
CleanArchitecture.gRPC.Tests/Fixtures/UserTestsFixture.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CleanArchitecture.Domain.Entities;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
using MockQueryable.Moq;
|
||||
using Moq;
|
||||
|
||||
namespace CleanArchitecture.gRPC.Tests.Fixtures;
|
||||
|
||||
public sealed class UserTestsFixture
|
||||
{
|
||||
private Mock<IUserRepository> UserRepository { get; } = new ();
|
||||
|
||||
public UsersApiImplementation UsersApiImplementation { get; }
|
||||
|
||||
public IEnumerable<User> ExistingUsers { get; }
|
||||
|
||||
public UserTestsFixture()
|
||||
{
|
||||
ExistingUsers = new List<User>()
|
||||
{
|
||||
new (Guid.NewGuid(), "test@test.de", "Test First Name", "Test Last Name"),
|
||||
new (Guid.NewGuid(), "email@Email.de", "Email First Name", "Email Last Name"),
|
||||
new (Guid.NewGuid(), "user@user.de", "User First Name", "User Last Name"),
|
||||
};
|
||||
|
||||
var queryable = ExistingUsers.AsQueryable().BuildMock();
|
||||
|
||||
UserRepository
|
||||
.Setup(repository => repository.GetAllNoTracking())
|
||||
.Returns(queryable);
|
||||
|
||||
UsersApiImplementation = new UsersApiImplementation(UserRepository.Object);
|
||||
}
|
||||
}
|
74
CleanArchitecture.gRPC.Tests/Users/GetUsersByIdsTests.cs
Normal file
74
CleanArchitecture.gRPC.Tests/Users/GetUsersByIdsTests.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.gRPC.Tests.Fixtures;
|
||||
using CleanArchitecture.Proto.Users;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace CleanArchitecture.gRPC.Tests.Users;
|
||||
|
||||
public sealed class GetUsersByIdsTests : IClassFixture<UserTestsFixture>
|
||||
{
|
||||
private readonly UserTestsFixture _fixture;
|
||||
|
||||
public GetUsersByIdsTests(UserTestsFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_Get_Empty_List_If_No_Ids_Are_Given()
|
||||
{
|
||||
var result = await _fixture.UsersApiImplementation.GetByIds(
|
||||
SetupRequest(Enumerable.Empty<Guid>()),
|
||||
null!);
|
||||
|
||||
result.Users.Should().HaveCount(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_Get_Requested_Asked_Ids()
|
||||
{
|
||||
var nonExistingId = Guid.NewGuid();
|
||||
|
||||
var ids = _fixture.ExistingUsers
|
||||
.Take(2)
|
||||
.Select(user => user.Id)
|
||||
.ToList();
|
||||
|
||||
ids.Add(nonExistingId);
|
||||
|
||||
var result = await _fixture.UsersApiImplementation.GetByIds(
|
||||
SetupRequest(ids),
|
||||
null!);
|
||||
|
||||
result.Users.Should().HaveCount(2);
|
||||
|
||||
foreach (var user in result.Users)
|
||||
{
|
||||
var userId = Guid.Parse(user.Id);
|
||||
|
||||
userId.Should().NotBe(nonExistingId);
|
||||
|
||||
var mockUser = _fixture.ExistingUsers.First(u => u.Id == userId);
|
||||
|
||||
mockUser.Should().NotBeNull();
|
||||
|
||||
user.Email.Should().Be(mockUser.Email);
|
||||
user.FirstName.Should().Be(mockUser.GivenName);
|
||||
user.LastName.Should().Be(mockUser.Surname);
|
||||
}
|
||||
}
|
||||
|
||||
private static GetByIdsRequest SetupRequest(IEnumerable<Guid> ids)
|
||||
{
|
||||
var request = new GetByIdsRequest();
|
||||
|
||||
request.Ids.AddRange(ids.Select(id => id.ToString()));
|
||||
request.Ids.Add("Not a guid");
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
17
CleanArchitecture.gRPC/CleanArchitecture.gRPC.csproj
Normal file
17
CleanArchitecture.gRPC/CleanArchitecture.gRPC.csproj
Normal file
@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CleanArchitecture.Domain\CleanArchitecture.Domain.csproj" />
|
||||
<ProjectReference Include="..\CleanArchitecture.Proto\CleanArchitecture.Proto.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
54
CleanArchitecture.gRPC/UsersApiImplementation.cs
Normal file
54
CleanArchitecture.gRPC/UsersApiImplementation.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CleanArchitecture.Domain.Interfaces.Repositories;
|
||||
using CleanArchitecture.Proto.Users;
|
||||
using Grpc.Core;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CleanArchitecture.gRPC;
|
||||
|
||||
public sealed class UsersApiImplementation : UsersApi.UsersApiBase
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
|
||||
public UsersApiImplementation(IUserRepository userRepository)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public override async Task<GetByIdsResult> GetByIds(
|
||||
GetByIdsRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
var idsAsGuids = new List<Guid>(request.Ids.Count);
|
||||
|
||||
foreach (var id in request.Ids)
|
||||
{
|
||||
if (Guid.TryParse(id, out var parsed))
|
||||
{
|
||||
idsAsGuids.Add(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
var users = await _userRepository
|
||||
.GetAllNoTracking()
|
||||
.Where(user => idsAsGuids.Contains(user.Id))
|
||||
.Select(user => new GrpcUser
|
||||
{
|
||||
Id = user.Id.ToString(),
|
||||
Email = user.Email,
|
||||
FirstName = user.GivenName,
|
||||
LastName = user.Surname,
|
||||
IsDeleted = user.Deleted
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var result = new GetByIdsResult();
|
||||
|
||||
result.Users.AddRange(users);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -16,6 +16,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Infrastru
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.IntegrationTests", "CleanArchitecture.IntegrationTests\CleanArchitecture.IntegrationTests.csproj", "{39732BD4-909F-410C-8737-1F9FE3E269A7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.gRPC", "CleanArchitecture.gRPC\CleanArchitecture.gRPC.csproj", "{7A6353A9-B60C-4B13-A849-D21B315047EE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.Proto", "CleanArchitecture.Proto\CleanArchitecture.Proto.csproj", "{5F978903-7A7A-45C2-ABE0-C2906ECD326B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanArchitecture.gRPC.Tests", "CleanArchitecture.gRPC.Tests\CleanArchitecture.gRPC.Tests.csproj", "{E3A836DD-85DB-44FD-BC19-DDFE111D9EB0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -54,5 +60,17 @@ Global
|
||||
{39732BD4-909F-410C-8737-1F9FE3E269A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{39732BD4-909F-410C-8737-1F9FE3E269A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{39732BD4-909F-410C-8737-1F9FE3E269A7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7A6353A9-B60C-4B13-A849-D21B315047EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7A6353A9-B60C-4B13-A849-D21B315047EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7A6353A9-B60C-4B13-A849-D21B315047EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7A6353A9-B60C-4B13-A849-D21B315047EE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5F978903-7A7A-45C2-ABE0-C2906ECD326B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5F978903-7A7A-45C2-ABE0-C2906ECD326B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5F978903-7A7A-45C2-ABE0-C2906ECD326B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5F978903-7A7A-45C2-ABE0-C2906ECD326B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E3A836DD-85DB-44FD-BC19-DDFE111D9EB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E3A836DD-85DB-44FD-BC19-DDFE111D9EB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E3A836DD-85DB-44FD-BC19-DDFE111D9EB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E3A836DD-85DB-44FD-BC19-DDFE111D9EB0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
Loading…
Reference in New Issue
Block a user