diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs index e8b009949a..0cf35e54e9 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs @@ -247,7 +247,7 @@ namespace Volo.Abp.AspNetCore.Mvc protected virtual string CalculateUrl(string rootPath, string controllerName, ActionModel action, string httpMethod) { - var url = $"api/{rootPath}/{controllerName}"; + var url = $"api/{rootPath}/{controllerName.ToCamelCase()}"; //Add {id} path if needed if (action.Parameters.Any(p => p.ParameterName == "id")) diff --git a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs index d582b2fa15..0d3d63f92b 100644 --- a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs +++ b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs @@ -112,5 +112,10 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore DbSet.Remove(entity); } } + + public override Task GetCountAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + return GetQueryable().LongCountAsync(cancellationToken); + } } } diff --git a/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IUserAppService.cs b/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IUserAppService.cs index 4d6102464d..72458e5f2d 100644 --- a/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IUserAppService.cs +++ b/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IUserAppService.cs @@ -1,14 +1,11 @@ using System; -using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; namespace Volo.Abp.Identity { - public interface IUserAppService : IApplicationService + public interface IUserAppService : IAsyncCrudAppService { - Task> Get(); - - Task Get(Guid id); + } } diff --git a/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDto.cs b/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDto.cs new file mode 100644 index 0000000000..e289475e4b --- /dev/null +++ b/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDto.cs @@ -0,0 +1,17 @@ +namespace Volo.Abp.Identity +{ + public class IdentityUserCreateOrUpdateDto + { + public string UserName { get; set; } + + public string Email { get; set; } + + public string PhoneNumber { get; set; } + + public bool TwoFactorEnabled { get; set; } + + public bool LockoutEnabled { get; set; } + + public string Password { get; set; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs b/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs index 4dc78fd213..47ce01f194 100644 --- a/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs +++ b/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Application.Dtos; +using System; +using Volo.Abp.Application.Dtos; namespace Volo.Abp.Identity { @@ -7,5 +8,17 @@ namespace Volo.Abp.Identity public string UserName { get; set; } public string Email { get; set; } + + public bool EmailConfirmed { get; set; } + + public string PhoneNumber { get; set; } + + public bool PhoneNumberConfirmed { get; set; } + + public bool TwoFactorEnabled { get; set; } + + public DateTimeOffset? LockoutEnd { get; set; } + + public bool LockoutEnabled { get; set; } } } \ No newline at end of file diff --git a/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/UserAppService.cs b/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/UserAppService.cs index 39b35359fb..4d5e448787 100644 --- a/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/UserAppService.cs +++ b/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/UserAppService.cs @@ -10,27 +10,66 @@ namespace Volo.Abp.Identity public class UserAppService : ApplicationService, IUserAppService { + private readonly IdentityUserManager _userManager; private readonly IIdentityUserRepository _userRepository; - public UserAppService(IIdentityUserRepository userRepository) + public UserAppService(IdentityUserManager userManager, IIdentityUserRepository userRepository) { + _userManager = userManager; _userRepository = userRepository; } - public async Task> Get() + public async Task GetAsync(Guid id) { - var users = await _userRepository.GetListAsync(); + return ObjectMapper.Map( + await _userManager.GetByIdAsync(id) + ); + } - return new ListResultDto( - ObjectMapper.Map, List>(users) + public async Task> GetListAsync(PagedAndSortedResultRequestDto input) + { + var userCount = (int) await _userRepository.GetCountAsync(); + var userDtos = ObjectMapper.Map, List>( + await _userRepository.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount) ); + + return new PagedResultDto(userCount, userDtos); } - public async Task Get(Guid id) + public async Task CreateAsync(IdentityUserCreateOrUpdateDto input) { - var user = await _userRepository.GetAsync(id); + var user = new IdentityUser(GuidGenerator.Create(), input.UserName); + + await UpdateUserProperties(input, user); + await _userManager.CreateAsync(user, input.Password); + await CurrentUnitOfWork.SaveChangesAsync(); return ObjectMapper.Map(user); } + + public async Task UpdateAsync(Guid id, IdentityUserCreateOrUpdateDto input) + { + var user = await _userManager.GetByIdAsync(id); + + await _userManager.SetUserNameAsync(user, input.UserName); + await UpdateUserProperties(input, user); + await CurrentUnitOfWork.SaveChangesAsync(); + + return ObjectMapper.Map(user); + } + + public async Task DeleteAsync(Guid id) + { + var user = await _userManager.GetByIdAsync(id); + await _userManager.DeleteAsync(user); + } + + private async Task UpdateUserProperties(IdentityUserCreateOrUpdateDto input, IdentityUser user) + { + await _userManager.SetEmailAsync(user, input.Email); + await _userManager.SetPhoneNumberAsync(user, input.PhoneNumber); + await _userManager.SetTwoFactorEnabledAsync(user, input.TwoFactorEnabled); + await _userManager.SetLockoutEnabledAsync(user, input.LockoutEnabled); + } } } diff --git a/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs b/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs index 2fb3f4f5ce..632a9028fb 100644 --- a/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs +++ b/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading; using System.Linq; +using System.Linq.Dynamic.Core; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; @@ -76,5 +77,10 @@ namespace Volo.Abp.Identity return await query.ToListAsync(cancellationToken); } + + public async Task> GetListAsync(string sorting, int maxResultCount, int skipCount) + { + return await this.OrderBy(sorting ?? "UserName").PageBy(skipCount, maxResultCount).ToListAsync(); + } } } diff --git a/src/Volo.Abp.Identity.Web/Areas/Identity/Controllers/UsersController.cs b/src/Volo.Abp.Identity.Web/Areas/Identity/Controllers/UsersController.cs index 39996939bb..eac9a29cb1 100644 --- a/src/Volo.Abp.Identity.Web/Areas/Identity/Controllers/UsersController.cs +++ b/src/Volo.Abp.Identity.Web/Areas/Identity/Controllers/UsersController.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Dtos; using Volo.Abp.AspNetCore.Mvc; namespace Volo.Abp.Identity.Web.Areas.Identity.Controllers @@ -16,7 +17,7 @@ namespace Volo.Abp.Identity.Web.Areas.Identity.Controllers public async Task Index() { - var result = await _userAppService.Get(); + var result = await _userAppService.GetListAsync(new PagedAndSortedResultRequestDto()); return View(result.Items); } } diff --git a/src/Volo.Abp.Identity/Volo/Abp/Identity/IIdentityUserRepository.cs b/src/Volo.Abp.Identity/Volo/Abp/Identity/IIdentityUserRepository.cs index 0342e800cf..e28253d081 100644 --- a/src/Volo.Abp.Identity/Volo/Abp/Identity/IIdentityUserRepository.cs +++ b/src/Volo.Abp.Identity/Volo/Abp/Identity/IIdentityUserRepository.cs @@ -18,8 +18,12 @@ namespace Volo.Abp.Identity Task FindByNormalizedEmailAsync([NotNull] string normalizedEmail, CancellationToken cancellationToken); + //TODO: Why not return List instead of IList Task> GetListByClaimAsync(Claim claim, CancellationToken cancellationToken); + //TODO: Why not return List instead of IList Task> GetListByNormalizedRoleNameAsync(string normalizedRoleName, CancellationToken cancellationToken); + + Task> GetListAsync(string sorting, int maxResultCount, int skipCount); } } diff --git a/src/Volo.Abp.Identity/Volo/Abp/Identity/IdentityUserManager.cs b/src/Volo.Abp.Identity/Volo/Abp/Identity/IdentityUserManager.cs index dd749cd20c..6f6cbfc0d2 100644 --- a/src/Volo.Abp.Identity/Volo/Abp/Identity/IdentityUserManager.cs +++ b/src/Volo.Abp.Identity/Volo/Abp/Identity/IdentityUserManager.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Services; namespace Volo.Abp.Identity @@ -31,5 +33,16 @@ namespace Volo.Abp.Identity { } + + public async Task GetByIdAsync(Guid id) + { + var user = await Store.FindByIdAsync(id.ToString(), CancellationToken); + if (user == null) + { + throw new EntityNotFoundException(typeof(IdentityUser), id); + } + + return user; + } } } diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index afb2bbcfe5..dfb6022556 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -120,5 +120,10 @@ namespace Volo.Abp.Domain.Repositories.MongoDB { return Collection.AsQueryable(); } + + public override Task GetCountAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + return Collection.CountAsync(Builders.Filter.Empty, cancellationToken: cancellationToken); + } } } \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs b/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs index 99a7a42bed..a94d0ce554 100644 --- a/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs +++ b/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs @@ -18,28 +18,27 @@ namespace Volo.Abp.Application.Services } - public interface IAsyncCrudAppService - : IAsyncCrudAppService + public interface IAsyncCrudAppService + : IAsyncCrudAppService where TEntityDto : IEntityDto { } - public interface IAsyncCrudAppService - : IAsyncCrudAppService + public interface IAsyncCrudAppService + : IAsyncCrudAppService where TEntityDto : IEntityDto - where TCreateInput : IEntityDto { } - public interface IAsyncCrudAppService + public interface IAsyncCrudAppService : IApplicationService where TEntityDto : IEntityDto { Task GetAsync(TPrimaryKey id); - Task> GetListAsync(TGetAllInput input); + Task> GetListAsync(TGetListInput input); Task CreateAsync(TCreateInput input); diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/ICrudAppService.cs b/src/Volo.Abp/Volo/Abp/Application/Services/ICrudAppService.cs index 24a74e6cdd..a67aa83210 100644 --- a/src/Volo.Abp/Volo/Abp/Application/Services/ICrudAppService.cs +++ b/src/Volo.Abp/Volo/Abp/Application/Services/ICrudAppService.cs @@ -17,28 +17,27 @@ namespace Volo.Abp.Application.Services } - public interface ICrudAppService - : ICrudAppService + public interface ICrudAppService + : ICrudAppService where TEntityDto : IEntityDto { } - public interface ICrudAppService - : ICrudAppService + public interface ICrudAppService + : ICrudAppService where TEntityDto : IEntityDto - where TCreateInput : IEntityDto { } - public interface ICrudAppService + public interface ICrudAppService : IApplicationService where TEntityDto : IEntityDto { TEntityDto Get(TPrimaryKey id); - PagedResultDto GetAll(TGetAllInput input); + PagedResultDto GetAll(TGetListInput input); TEntityDto Create(TCreateInput input); diff --git a/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs b/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs index fe2771ce50..c432d66bb5 100644 --- a/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs +++ b/src/Volo.Abp/Volo/Abp/Domain/Repositories/IRepository.cs @@ -133,5 +133,18 @@ namespace Volo.Abp.Domain.Repositories /// A to observe while waiting for the task to complete. /// Primary key of the entity Task DeleteAsync(TPrimaryKey id, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Get list of all entities without any filtering. + /// + /// List of entities + long GetCount(); + + /// + /// Get list of all entities without any filtering. + /// + /// A to observe while waiting for the task to complete. + /// List of entities + Task GetCountAsync(CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/src/Volo.Abp/Volo/Abp/Domain/Repositories/QueryableRepositoryBase.cs b/src/Volo.Abp/Volo/Abp/Domain/Repositories/QueryableRepositoryBase.cs index 46af8d8e62..32911a9af0 100644 --- a/src/Volo.Abp/Volo/Abp/Domain/Repositories/QueryableRepositoryBase.cs +++ b/src/Volo.Abp/Volo/Abp/Domain/Repositories/QueryableRepositoryBase.cs @@ -59,5 +59,10 @@ namespace Volo.Abp.Domain.Repositories Delete(predicate); return Task.CompletedTask; } + + public override long GetCount() + { + return GetQueryable().LongCount(); + } } } \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Domain/Repositories/RepositoryBase.cs b/src/Volo.Abp/Volo/Abp/Domain/Repositories/RepositoryBase.cs index edf583c1a9..370ae8812e 100644 --- a/src/Volo.Abp/Volo/Abp/Domain/Repositories/RepositoryBase.cs +++ b/src/Volo.Abp/Volo/Abp/Domain/Repositories/RepositoryBase.cs @@ -86,6 +86,13 @@ namespace Volo.Abp.Domain.Repositories return Task.CompletedTask; } + public abstract long GetCount(); + + public virtual Task GetCountAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(GetCount()); + } + protected static Expression> CreateEqualityExpressionForId(TPrimaryKey id) { var lambdaParam = Expression.Parameter(typeof(TEntity)); diff --git a/test/Volo.Abp.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs b/test/Volo.Abp.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs index bfc6f5e0a0..3c6ec3e261 100644 --- a/test/Volo.Abp.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs +++ b/test/Volo.Abp.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs @@ -165,6 +165,11 @@ namespace Volo.Abp.Domain.Repositories throw new NotImplementedException(); } + public override long GetCount() + { + throw new NotImplementedException(); + } + public override List GetList() { throw new NotImplementedException();