From ffe44ba70c9e5c6a01179c7e2f4185543cbc441c Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 7 Jan 2021 20:12:00 +0800 Subject: refactor: Make mapper a service. Fix #202. --- BackEnd/Timeline.ErrorCodes/ErrorCodes.cs | 4 +- BackEnd/Timeline.Tests/Helpers/TestDatabase.cs | 5 - .../IntegratedTests/HighlightTimelineTest.cs | 2 - BackEnd/Timeline/Auth/MyAuthenticationHandler.cs | 8 +- .../Controllers/BookmarkTimelineController.cs | 6 +- .../Controllers/HighlightTimelineController.cs | 9 +- BackEnd/Timeline/Controllers/TimelineController.cs | 18 +- BackEnd/Timeline/Controllers/TokenController.cs | 17 +- BackEnd/Timeline/Controllers/UserController.cs | 19 +- BackEnd/Timeline/Entities/UserEntity.cs | 12 +- .../20201217093401_AddHighlightTimelines.cs | 4 +- ...210107074715_AddRootUserPermissions.Designer.cs | 498 --------------------- .../20210107074715_AddRootUserPermissions.cs | 20 - .../Mapper/MapperServiceCollectionExtensions.cs | 13 + BackEnd/Timeline/Models/Mapper/TimelineMapper.cs | 55 ++- BackEnd/Timeline/Models/Mapper/UserMapper.cs | 31 +- BackEnd/Timeline/Models/Timeline.cs | 5 +- BackEnd/Timeline/Services/TimelinePostService.cs | 9 +- BackEnd/Timeline/Services/TimelineService.cs | 12 +- BackEnd/Timeline/Services/UserService.cs | 8 +- BackEnd/Timeline/Services/UserTokenManager.cs | 1 - BackEnd/Timeline/Startup.cs | 2 + 22 files changed, 147 insertions(+), 611 deletions(-) delete mode 100644 BackEnd/Timeline/Migrations/20210107074715_AddRootUserPermissions.Designer.cs delete mode 100644 BackEnd/Timeline/Migrations/20210107074715_AddRootUserPermissions.cs create mode 100644 BackEnd/Timeline/Models/Mapper/MapperServiceCollectionExtensions.cs diff --git a/BackEnd/Timeline.ErrorCodes/ErrorCodes.cs b/BackEnd/Timeline.ErrorCodes/ErrorCodes.cs index b8ec63ec..53a03b69 100644 --- a/BackEnd/Timeline.ErrorCodes/ErrorCodes.cs +++ b/BackEnd/Timeline.ErrorCodes/ErrorCodes.cs @@ -1,6 +1,4 @@ -using System; - -namespace Timeline.Models.Http +namespace Timeline.Models.Http { /// /// All error code constants. diff --git a/BackEnd/Timeline.Tests/Helpers/TestDatabase.cs b/BackEnd/Timeline.Tests/Helpers/TestDatabase.cs index 7b9a992f..c51a94ba 100644 --- a/BackEnd/Timeline.Tests/Helpers/TestDatabase.cs +++ b/BackEnd/Timeline.Tests/Helpers/TestDatabase.cs @@ -1,8 +1,6 @@ using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using System; using System.Threading.Tasks; using Timeline.Entities; using Timeline.Migrations; @@ -42,9 +40,6 @@ namespace Timeline.Tests.Helpers var admin = await userService.CreateUser("admin", "adminpw"); await userService.ModifyUser(admin.Id, new ModifyUserParams() { Nickname = "administrator" }); - admin.Permissions.Add(new UserPermissionEntity { Permission = UserPermission.AllTimelineManagement.ToString() }); - admin.Permissions.Add(new UserPermissionEntity { Permission = UserPermission.HighlightTimelineManagement.ToString() }); - admin.Permissions.Add(new UserPermissionEntity { Permission = UserPermission.UserManagement.ToString() }); await context.SaveChangesAsync(); if (_createUser) diff --git a/BackEnd/Timeline.Tests/IntegratedTests/HighlightTimelineTest.cs b/BackEnd/Timeline.Tests/IntegratedTests/HighlightTimelineTest.cs index 63f40a1e..a3f2855e 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests/HighlightTimelineTest.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests/HighlightTimelineTest.cs @@ -1,7 +1,5 @@ using FluentAssertions; -using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Timeline.Models.Http; using Xunit; diff --git a/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs b/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs index c57c96bc..e4122c65 100644 --- a/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs +++ b/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs @@ -32,12 +32,14 @@ namespace Timeline.Auth { private readonly ILogger _logger; private readonly IUserTokenManager _userTokenManager; + private readonly IUserPermissionService _userPermissionService; - public MyAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserTokenManager userTokenManager) + public MyAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserTokenManager userTokenManager, IUserPermissionService userPermissionService) : base(options, logger, encoder, clock) { _logger = logger.CreateLogger(); _userTokenManager = userTokenManager; + _userPermissionService = userPermissionService; } // return null if no token is found @@ -84,7 +86,9 @@ namespace Timeline.Auth var identity = new ClaimsIdentity(AuthenticationConstants.Scheme); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64)); identity.AddClaim(new Claim(identity.NameClaimType, user.Username, ClaimValueTypes.String)); - identity.AddClaims(user.Permissions.Select(permission => new Claim(AuthenticationConstants.PermissionClaimName, permission.Permission, ClaimValueTypes.String))); + + var permissions = await _userPermissionService.GetPermissionsOfUserAsync(user.Id); + identity.AddClaims(permissions.Select(permission => new Claim(AuthenticationConstants.PermissionClaimName, permission.ToString(), ClaimValueTypes.String))); var principal = new ClaimsPrincipal(); principal.AddIdentity(identity); diff --git a/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs b/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs index 7412232d..64cb8afa 100644 --- a/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs +++ b/BackEnd/Timeline/Controllers/BookmarkTimelineController.cs @@ -19,11 +19,13 @@ namespace Timeline.Controllers { private readonly IBookmarkTimelineService _service; private readonly ITimelineService _timelineService; + private readonly TimelineMapper _timelineMapper; - public BookmarkTimelineController(IBookmarkTimelineService service, ITimelineService timelineService) + public BookmarkTimelineController(IBookmarkTimelineService service, ITimelineService timelineService, TimelineMapper timelineMapper) { _service = service; _timelineService = timelineService; + _timelineMapper = timelineMapper; } /// @@ -38,7 +40,7 @@ namespace Timeline.Controllers { var ids = await _service.GetBookmarks(this.GetUserId()); var timelines = await _timelineService.GetTimelineList(ids); - return Ok(timelines.MapToHttp(Url)); + return await _timelineMapper.MapToHttp(timelines, Url); } /// diff --git a/BackEnd/Timeline/Controllers/HighlightTimelineController.cs b/BackEnd/Timeline/Controllers/HighlightTimelineController.cs index 76650b00..685ec16f 100644 --- a/BackEnd/Timeline/Controllers/HighlightTimelineController.cs +++ b/BackEnd/Timeline/Controllers/HighlightTimelineController.cs @@ -1,5 +1,4 @@ -using AutoMapper; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Threading.Tasks; using Timeline.Auth; @@ -20,11 +19,13 @@ namespace Timeline.Controllers { private readonly IHighlightTimelineService _service; private readonly ITimelineService _timelineService; + private readonly TimelineMapper _timelineMapper; - public HighlightTimelineController(IHighlightTimelineService service, ITimelineService timelineService) + public HighlightTimelineController(IHighlightTimelineService service, ITimelineService timelineService, TimelineMapper timelineMapper) { _service = service; _timelineService = timelineService; + _timelineMapper = timelineMapper; } /// @@ -37,7 +38,7 @@ namespace Timeline.Controllers { var ids = await _service.GetHighlightTimelines(); var timelines = await _timelineService.GetTimelineList(ids); - return timelines.MapToHttp(Url); + return await _timelineMapper.MapToHttp(timelines, Url); } /// diff --git a/BackEnd/Timeline/Controllers/TimelineController.cs b/BackEnd/Timeline/Controllers/TimelineController.cs index b1401a03..efc49952 100644 --- a/BackEnd/Timeline/Controllers/TimelineController.cs +++ b/BackEnd/Timeline/Controllers/TimelineController.cs @@ -30,16 +30,18 @@ namespace Timeline.Controllers private readonly ITimelineService _service; private readonly ITimelinePostService _postService; + private readonly TimelineMapper _timelineMapper; private readonly IMapper _mapper; /// /// /// - public TimelineController(IUserService userService, ITimelineService service, ITimelinePostService timelinePostService, IMapper mapper) + public TimelineController(IUserService userService, ITimelineService service, ITimelinePostService timelinePostService, TimelineMapper timelineMapper, IMapper mapper) { _userService = userService; _service = service; _postService = timelinePostService; + _timelineMapper = timelineMapper; _mapper = mapper; } @@ -107,7 +109,7 @@ namespace Timeline.Controllers } var timelines = await _service.GetTimelines(relationship, visibilityFilter); - var result = timelines.MapToHttp(Url); + var result = await _timelineMapper.MapToHttp(timelines, Url); return result; } @@ -166,7 +168,7 @@ namespace Timeline.Controllers else { var t = await _service.GetTimeline(timelineId); - var result = t.MapToHttp(Url); + var result = await _timelineMapper.MapToHttp(t, Url); return result; } } @@ -193,7 +195,7 @@ namespace Timeline.Controllers var posts = await _postService.GetPosts(timelineId, modifiedSince, includeDeleted ?? false); - var result = posts.MapToHttp(timeline, Url); + var result = await _timelineMapper.MapToHttp(posts, timeline, Url); return result; } @@ -304,7 +306,7 @@ namespace Timeline.Controllers return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ContentUnknownType)); } - var result = post.MapToHttp(timeline, Url); + var result = await _timelineMapper.MapToHttp(post, timeline, Url); return result; } @@ -361,7 +363,7 @@ namespace Timeline.Controllers } await _service.ChangeProperty(timelineId, _mapper.Map(body)); var t = await _service.GetTimeline(timelineId); - var result = t.MapToHttp(Url); + var result = await _timelineMapper.MapToHttp(t, Url); return result; } @@ -446,7 +448,7 @@ namespace Timeline.Controllers try { var timeline = await _service.CreateTimeline(body.Name, userId); - var result = timeline.MapToHttp(Url); + var result = await _timelineMapper.MapToHttp(timeline, Url); return result; } catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.Timeline) @@ -505,7 +507,7 @@ namespace Timeline.Controllers { await _service.ChangeTimelineName(timelineId, body.NewName); var timeline = await _service.GetTimeline(timelineId); - return Ok(timeline.MapToHttp(Url)); + return await _timelineMapper.MapToHttp(timeline, Url); } catch (EntityAlreadyExistException) { diff --git a/BackEnd/Timeline/Controllers/TokenController.cs b/BackEnd/Timeline/Controllers/TokenController.cs index e695a10e..b3675aad 100644 --- a/BackEnd/Timeline/Controllers/TokenController.cs +++ b/BackEnd/Timeline/Controllers/TokenController.cs @@ -1,4 +1,3 @@ -using AutoMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -26,14 +25,16 @@ namespace Timeline.Controllers private readonly IUserCredentialService _userCredentialService; private readonly IUserTokenManager _userTokenManager; private readonly ILogger _logger; + private readonly UserMapper _userMapper; private readonly IClock _clock; /// - public TokenController(IUserCredentialService userCredentialService, IUserTokenManager userTokenManager, ILogger logger, IClock clock) + public TokenController(IUserCredentialService userCredentialService, IUserTokenManager userTokenManager, ILogger logger, UserMapper userMapper, IClock clock) { _userCredentialService = userCredentialService; _userTokenManager = userTokenManager; _logger = logger; + _userMapper = userMapper; _clock = clock; } @@ -69,11 +70,11 @@ namespace Timeline.Controllers ("Username", request.Username), ("Expire At", expireTime?.ToString(CultureInfo.CurrentCulture.DateTimeFormat) ?? "default") )); - return Ok(new HttpCreateTokenResponse + return new HttpCreateTokenResponse { Token = result.Token, - User = result.User.MapToHttp(Url) - }); + User = await _userMapper.MapToHttp(result.User, Url) + }; } catch (UserNotExistException e) { @@ -111,10 +112,10 @@ namespace Timeline.Controllers var result = await _userTokenManager.VerifyToken(request.Token); _logger.LogInformation(Log.Format(LogVerifySuccess, ("Username", result.Username), ("Token", request.Token))); - return Ok(new HttpVerifyTokenResponse + return new HttpVerifyTokenResponse { - User = result.MapToHttp(Url) - }); + User = await _userMapper.MapToHttp(result, Url) + }; } catch (UserTokenTimeExpireException e) { diff --git a/BackEnd/Timeline/Controllers/UserController.cs b/BackEnd/Timeline/Controllers/UserController.cs index 93b17b2e..e1a9d454 100644 --- a/BackEnd/Timeline/Controllers/UserController.cs +++ b/BackEnd/Timeline/Controllers/UserController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; +using System.Collections.Generic; using System.Threading.Tasks; using Timeline.Auth; using Timeline.Helpers; @@ -28,16 +29,18 @@ namespace Timeline.Controllers private readonly IUserCredentialService _userCredentialService; private readonly IUserPermissionService _userPermissionService; private readonly IUserDeleteService _userDeleteService; + private readonly UserMapper _userMapper; private readonly IMapper _mapper; /// - public UserController(ILogger logger, IUserService userService, IUserCredentialService userCredentialService, IUserPermissionService userPermissionService, IUserDeleteService userDeleteService, IMapper mapper) + public UserController(ILogger logger, IUserService userService, IUserCredentialService userCredentialService, IUserPermissionService userPermissionService, IUserDeleteService userDeleteService, UserMapper userMapper, IMapper mapper) { _logger = logger; _userService = userService; _userCredentialService = userCredentialService; _userPermissionService = userPermissionService; _userDeleteService = userDeleteService; + _userMapper = userMapper; _mapper = mapper; } @@ -49,11 +52,11 @@ namespace Timeline.Controllers /// All user list. [HttpGet("users")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> List() + public async Task>> List() { var users = await _userService.GetUsers(); - var result = users.MapToHttp(Url); - return Ok(result); + var result = await _userMapper.MapToHttp(users, Url); + return result; } /// @@ -70,7 +73,7 @@ namespace Timeline.Controllers { var id = await _userService.GetUserIdByUsername(username); var user = await _userService.GetUser(id); - return Ok(user.MapToHttp(Url)); + return await _userMapper.MapToHttp(user, Url); } catch (UserNotExistException e) { @@ -99,7 +102,7 @@ namespace Timeline.Controllers { var id = await _userService.GetUserIdByUsername(username); var user = await _userService.ModifyUser(id, _mapper.Map(body)); - return Ok(user.MapToHttp(Url)); + return await _userMapper.MapToHttp(user, Url); } catch (UserNotExistException e) { @@ -126,7 +129,7 @@ namespace Timeline.Controllers ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Password)); var user = await _userService.ModifyUser(this.GetUserId(), _mapper.Map(body)); - return Ok(user.MapToHttp(Url)); + return await _userMapper.MapToHttp(user, Url); } } @@ -170,7 +173,7 @@ namespace Timeline.Controllers try { var user = await _userService.CreateUser(body.Username, body.Password); - return Ok(user.MapToHttp(Url)); + return await _userMapper.MapToHttp(user, Url); } catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.User) { diff --git a/BackEnd/Timeline/Entities/UserEntity.cs b/BackEnd/Timeline/Entities/UserEntity.cs index 6a256a31..ad4d7db5 100644 --- a/BackEnd/Timeline/Entities/UserEntity.cs +++ b/BackEnd/Timeline/Entities/UserEntity.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.AspNetCore.Mvc; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -38,6 +39,10 @@ namespace Timeline.Entities public UserAvatarEntity? Avatar { get; set; } #pragma warning disable CA2227 // Collection properties should be read only + /// + /// Do not use this directly. Get permissions with . + /// + [Obsolete("Use IUserPermissionService instead.")] public List Permissions { get; set; } = default!; public List Timelines { get; set; } = default!; @@ -45,6 +50,11 @@ namespace Timeline.Entities public List TimelinePosts { get; set; } = default!; public List TimelinesJoined { get; set; } = default!; + + internal object MapToHttp(IUrlHelper url) + { + throw new NotImplementedException(); + } #pragma warning restore CA2227 // Collection properties should be read only } } diff --git a/BackEnd/Timeline/Migrations/20201217093401_AddHighlightTimelines.cs b/BackEnd/Timeline/Migrations/20201217093401_AddHighlightTimelines.cs index e838615e..f04a361d 100644 --- a/BackEnd/Timeline/Migrations/20201217093401_AddHighlightTimelines.cs +++ b/BackEnd/Timeline/Migrations/20201217093401_AddHighlightTimelines.cs @@ -1,5 +1,5 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; +using System; namespace Timeline.Migrations { diff --git a/BackEnd/Timeline/Migrations/20210107074715_AddRootUserPermissions.Designer.cs b/BackEnd/Timeline/Migrations/20210107074715_AddRootUserPermissions.Designer.cs deleted file mode 100644 index 6cd41998..00000000 --- a/BackEnd/Timeline/Migrations/20210107074715_AddRootUserPermissions.Designer.cs +++ /dev/null @@ -1,498 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Timeline.Entities; - -namespace Timeline.Migrations -{ - [DbContext(typeof(DatabaseContext))] - [Migration("20210107074715_AddRootUserPermissions")] - partial class AddRootUserPermissions - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "5.0.0"); - - modelBuilder.Entity("Timeline.Entities.BookmarkTimelineEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasColumnName("id"); - - b.Property("Rank") - .HasColumnType("INTEGER") - .HasColumnName("rank"); - - b.Property("TimelineId") - .HasColumnType("INTEGER") - .HasColumnName("timeline"); - - b.Property("UserId") - .HasColumnType("INTEGER") - .HasColumnName("user"); - - b.HasKey("Id"); - - b.HasIndex("TimelineId"); - - b.HasIndex("UserId"); - - b.ToTable("bookmark_timelines"); - }); - - modelBuilder.Entity("Timeline.Entities.DataEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasColumnName("id"); - - b.Property("Data") - .IsRequired() - .HasColumnType("BLOB") - .HasColumnName("data"); - - b.Property("Ref") - .HasColumnType("INTEGER") - .HasColumnName("ref"); - - b.Property("Tag") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("tag"); - - b.HasKey("Id"); - - b.HasIndex("Tag") - .IsUnique(); - - b.ToTable("data"); - }); - - modelBuilder.Entity("Timeline.Entities.HighlightTimelineEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasColumnName("id"); - - b.Property("AddTime") - .HasColumnType("TEXT") - .HasColumnName("add_time"); - - b.Property("OperatorId") - .HasColumnType("INTEGER") - .HasColumnName("operator_id"); - - b.Property("Order") - .HasColumnType("INTEGER") - .HasColumnName("order"); - - b.Property("TimelineId") - .HasColumnType("INTEGER") - .HasColumnName("timeline_id"); - - b.HasKey("Id"); - - b.HasIndex("OperatorId"); - - b.HasIndex("TimelineId"); - - b.ToTable("highlight_timelines"); - }); - - modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasColumnName("id"); - - b.Property("Key") - .IsRequired() - .HasColumnType("BLOB") - .HasColumnName("key"); - - b.HasKey("Id"); - - b.ToTable("jwt_token"); - }); - - modelBuilder.Entity("Timeline.Entities.TimelineEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasColumnName("id"); - - b.Property("CreateTime") - .HasColumnType("TEXT") - .HasColumnName("create_time"); - - b.Property("CurrentPostLocalId") - .HasColumnType("INTEGER") - .HasColumnName("current_post_local_id"); - - b.Property("Description") - .HasColumnType("TEXT") - .HasColumnName("description"); - - b.Property("LastModified") - .HasColumnType("TEXT") - .HasColumnName("last_modified"); - - b.Property("Name") - .HasColumnType("TEXT") - .HasColumnName("name"); - - b.Property("NameLastModified") - .HasColumnType("TEXT") - .HasColumnName("name_last_modified"); - - b.Property("OwnerId") - .HasColumnType("INTEGER") - .HasColumnName("owner"); - - b.Property("Title") - .HasColumnType("TEXT") - .HasColumnName("title"); - - b.Property("UniqueId") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("TEXT") - .HasColumnName("unique_id") - .HasDefaultValueSql("lower(hex(randomblob(16)))"); - - b.Property("Visibility") - .HasColumnType("INTEGER") - .HasColumnName("visibility"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("timelines"); - }); - - modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasColumnName("id"); - - b.Property("TimelineId") - .HasColumnType("INTEGER") - .HasColumnName("timeline"); - - b.Property("UserId") - .HasColumnType("INTEGER") - .HasColumnName("user"); - - b.HasKey("Id"); - - b.HasIndex("TimelineId"); - - b.HasIndex("UserId"); - - b.ToTable("timeline_members"); - }); - - modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasColumnName("id"); - - b.Property("AuthorId") - .HasColumnType("INTEGER") - .HasColumnName("author"); - - b.Property("Content") - .HasColumnType("TEXT") - .HasColumnName("content"); - - b.Property("ContentType") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("content_type"); - - b.Property("ExtraContent") - .HasColumnType("TEXT") - .HasColumnName("extra_content"); - - b.Property("LastUpdated") - .HasColumnType("TEXT") - .HasColumnName("last_updated"); - - b.Property("LocalId") - .HasColumnType("INTEGER") - .HasColumnName("local_id"); - - b.Property("Time") - .HasColumnType("TEXT") - .HasColumnName("time"); - - b.Property("TimelineId") - .HasColumnType("INTEGER") - .HasColumnName("timeline"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.HasIndex("TimelineId"); - - b.ToTable("timeline_posts"); - }); - - modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasColumnName("id"); - - b.Property("DataTag") - .HasColumnType("TEXT") - .HasColumnName("data_tag"); - - b.Property("LastModified") - .HasColumnType("TEXT") - .HasColumnName("last_modified"); - - b.Property("Type") - .HasColumnType("TEXT") - .HasColumnName("type"); - - b.Property("UserId") - .HasColumnType("INTEGER") - .HasColumnName("user"); - - b.HasKey("Id"); - - b.HasIndex("UserId") - .IsUnique(); - - b.ToTable("user_avatars"); - }); - - modelBuilder.Entity("Timeline.Entities.UserEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasColumnName("id"); - - b.Property("CreateTime") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT") - .HasColumnName("create_time") - .HasDefaultValueSql("datetime('now', 'utc')"); - - b.Property("LastModified") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT") - .HasColumnName("last_modified") - .HasDefaultValueSql("datetime('now', 'utc')"); - - b.Property("Nickname") - .HasColumnType("TEXT") - .HasColumnName("nickname"); - - b.Property("Password") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("password"); - - b.Property("UniqueId") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("TEXT") - .HasColumnName("unique_id") - .HasDefaultValueSql("lower(hex(randomblob(16)))"); - - b.Property("Username") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("username"); - - b.Property("UsernameChangeTime") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT") - .HasColumnName("username_change_time") - .HasDefaultValueSql("datetime('now', 'utc')"); - - b.Property("Version") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L) - .HasColumnName("version"); - - b.HasKey("Id"); - - b.HasIndex("Username") - .IsUnique(); - - b.ToTable("users"); - }); - - modelBuilder.Entity("Timeline.Entities.UserPermissionEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasColumnName("id"); - - b.Property("Permission") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("permission"); - - b.Property("UserId") - .HasColumnType("INTEGER") - .HasColumnName("user_id"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("user_permission"); - }); - - modelBuilder.Entity("Timeline.Entities.BookmarkTimelineEntity", b => - { - b.HasOne("Timeline.Entities.TimelineEntity", "Timeline") - .WithMany() - .HasForeignKey("TimelineId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Timeline.Entities.UserEntity", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Timeline"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Timeline.Entities.HighlightTimelineEntity", b => - { - b.HasOne("Timeline.Entities.UserEntity", "Operator") - .WithMany() - .HasForeignKey("OperatorId"); - - b.HasOne("Timeline.Entities.TimelineEntity", "Timeline") - .WithMany() - .HasForeignKey("TimelineId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Operator"); - - b.Navigation("Timeline"); - }); - - modelBuilder.Entity("Timeline.Entities.TimelineEntity", b => - { - b.HasOne("Timeline.Entities.UserEntity", "Owner") - .WithMany("Timelines") - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b => - { - b.HasOne("Timeline.Entities.TimelineEntity", "Timeline") - .WithMany("Members") - .HasForeignKey("TimelineId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Timeline.Entities.UserEntity", "User") - .WithMany("TimelinesJoined") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Timeline"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b => - { - b.HasOne("Timeline.Entities.UserEntity", "Author") - .WithMany("TimelinePosts") - .HasForeignKey("AuthorId"); - - b.HasOne("Timeline.Entities.TimelineEntity", "Timeline") - .WithMany("Posts") - .HasForeignKey("TimelineId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Author"); - - b.Navigation("Timeline"); - }); - - modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b => - { - b.HasOne("Timeline.Entities.UserEntity", "User") - .WithOne("Avatar") - .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Timeline.Entities.UserPermissionEntity", b => - { - b.HasOne("Timeline.Entities.UserEntity", "User") - .WithMany("Permissions") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Timeline.Entities.TimelineEntity", b => - { - b.Navigation("Members"); - - b.Navigation("Posts"); - }); - - modelBuilder.Entity("Timeline.Entities.UserEntity", b => - { - b.Navigation("Avatar"); - - b.Navigation("Permissions"); - - b.Navigation("TimelinePosts"); - - b.Navigation("Timelines"); - - b.Navigation("TimelinesJoined"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/BackEnd/Timeline/Migrations/20210107074715_AddRootUserPermissions.cs b/BackEnd/Timeline/Migrations/20210107074715_AddRootUserPermissions.cs deleted file mode 100644 index ff5db722..00000000 --- a/BackEnd/Timeline/Migrations/20210107074715_AddRootUserPermissions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using Timeline.Services; - -namespace Timeline.Migrations -{ - public partial class AddRootUserPermissions : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.InsertData("user_permission", new string[] { "user_id", "permission" }, new object[] { 1, UserPermission.UserManagement.ToString() }); - migrationBuilder.InsertData("user_permission", new string[] { "user_id", "permission" }, new object[] { 1, UserPermission.AllTimelineManagement.ToString() }); - migrationBuilder.InsertData("user_permission", new string[] { "user_id", "permission" }, new object[] { 1, UserPermission.HighlightTimelineManagement.ToString() }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - - } - } -} diff --git a/BackEnd/Timeline/Models/Mapper/MapperServiceCollectionExtensions.cs b/BackEnd/Timeline/Models/Mapper/MapperServiceCollectionExtensions.cs new file mode 100644 index 00000000..c87586d2 --- /dev/null +++ b/BackEnd/Timeline/Models/Mapper/MapperServiceCollectionExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Timeline.Models.Mapper +{ + public static class MapperServiceCollectionExtensions + { + public static void AddMappers(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + } + } +} diff --git a/BackEnd/Timeline/Models/Mapper/TimelineMapper.cs b/BackEnd/Timeline/Models/Mapper/TimelineMapper.cs index 89a5c0c8..14ca8fe9 100644 --- a/BackEnd/Timeline/Models/Mapper/TimelineMapper.cs +++ b/BackEnd/Timeline/Models/Mapper/TimelineMapper.cs @@ -1,5 +1,5 @@ -using AutoMapper; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -11,10 +11,22 @@ using Timeline.Services; namespace Timeline.Models.Mapper { - public static class TimelineMapper + public class TimelineMapper { - public static HttpTimeline MapToHttp(this TimelineEntity entity, IUrlHelper urlHelper) + private readonly DatabaseContext _database; + private readonly UserMapper _userMapper; + + public TimelineMapper(DatabaseContext database, UserMapper userMapper) + { + _database = database; + _userMapper = userMapper; + } + + public async Task MapToHttp(TimelineEntity entity, IUrlHelper urlHelper) { + await _database.Entry(entity).Reference(e => e.Owner).LoadAsync(); + await _database.Entry(entity).Collection(e => e.Members).Query().Include(m => m.User).LoadAsync(); + var timelineName = entity.Name is null ? "@" + entity.Owner.Username : entity.Name; return new HttpTimeline( @@ -23,9 +35,9 @@ namespace Timeline.Models.Mapper name: timelineName, nameLastModifed: entity.NameLastModified, description: entity.Description ?? "", - owner: entity.Owner.MapToHttp(urlHelper), + owner: await _userMapper.MapToHttp(entity.Owner, urlHelper), visibility: entity.Visibility, - members: entity.Members.Select(m => m.User.MapToHttp(urlHelper)).ToList(), + members: await _userMapper.MapToHttp(entity.Members.Select(m => m.User).ToList(), urlHelper), createTime: entity.CreateTime, lastModified: entity.LastModified, links: new HttpTimelineLinks( @@ -35,13 +47,18 @@ namespace Timeline.Models.Mapper ); } - public static List MapToHttp(this List entites, IUrlHelper urlHelper) + public async Task> MapToHttp(List entities, IUrlHelper urlHelper) { - return entites.Select(e => e.MapToHttp(urlHelper)).ToList(); + var result = new List(); + foreach (var entity in entities) + { + result.Add(await MapToHttp(entity, urlHelper)); + } + return result; } - public static HttpTimelinePost MapToHttp(this TimelinePostEntity entity, string timelineName, IUrlHelper urlHelper) + public async Task MapToHttp(TimelinePostEntity entity, string timelineName, IUrlHelper urlHelper) { HttpTimelinePostContent? content = null; @@ -67,19 +84,33 @@ namespace Timeline.Models.Mapper }; } + await _database.Entry(entity).Reference(e => e.Author).LoadAsync(); + + HttpUser? author = null; + + if (entity.Author is not null) + { + author = await _userMapper.MapToHttp(entity.Author, urlHelper); + } + return new HttpTimelinePost( id: entity.LocalId, content: content, deleted: content is null, time: entity.Time, - author: entity.Author?.MapToHttp(urlHelper), + author: author, lastUpdated: entity.LastUpdated ); } - public static List MapToHttp(this List entities, string timelineName, IUrlHelper urlHelper) + public async Task> MapToHttp(List entities, string timelineName, IUrlHelper urlHelper) { - return entities.Select(e => e.MapToHttp(timelineName, urlHelper)).ToList(); + var result = new List(); + foreach (var entity in entities) + { + result.Add(await MapToHttp(entity, timelineName, urlHelper)); + } + return result; } } } diff --git a/BackEnd/Timeline/Models/Mapper/UserMapper.cs b/BackEnd/Timeline/Models/Mapper/UserMapper.cs index 3255dca9..e6db4225 100644 --- a/BackEnd/Timeline/Models/Mapper/UserMapper.cs +++ b/BackEnd/Timeline/Models/Mapper/UserMapper.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; -using System.Linq; +using System.Threading.Tasks; using Timeline.Controllers; using Timeline.Entities; using Timeline.Models.Http; @@ -8,15 +8,24 @@ using Timeline.Services; namespace Timeline.Models.Mapper { - public static class UserMapper + public class UserMapper { - public static HttpUser MapToHttp(this UserEntity entity, IUrlHelper urlHelper) + private readonly DatabaseContext _database; + private readonly IUserPermissionService _userPermissionService; + + public UserMapper(DatabaseContext database, IUserPermissionService userPermissionService) + { + _database = database; + _userPermissionService = userPermissionService; + } + + public async Task MapToHttp(UserEntity entity, IUrlHelper urlHelper) { return new HttpUser( uniqueId: entity.UniqueId, username: entity.Username, nickname: string.IsNullOrEmpty(entity.Nickname) ? entity.Username : entity.Nickname, - permissions: MapPermission(entity), + permissions: (await _userPermissionService.GetPermissionsOfUserAsync(entity.Id, false)).ToStringList(), links: new HttpUserLinks( self: urlHelper.ActionLink(nameof(UserController.Get), nameof(UserController)[0..^nameof(Controller).Length], new { entity.Username }), avatar: urlHelper.ActionLink(nameof(UserAvatarController.Get), nameof(UserAvatarController)[0..^nameof(Controller).Length], new { entity.Username }), @@ -25,14 +34,14 @@ namespace Timeline.Models.Mapper ); } - public static List MapToHttp(this List entities, IUrlHelper urlHelper) - { - return entities.Select(e => e.MapToHttp(urlHelper)).ToList(); - } - - private static List MapPermission(UserEntity entity) + public async Task> MapToHttp(List entities, IUrlHelper urlHelper) { - return entity.Permissions.Select(p => p.Permission).ToList(); + var result = new List(); + foreach (var entity in entities) + { + result.Add(await MapToHttp(entity, urlHelper)); + } + return result; } } } diff --git a/BackEnd/Timeline/Models/Timeline.cs b/BackEnd/Timeline/Models/Timeline.cs index fa3c0eb3..9f3eabdf 100644 --- a/BackEnd/Timeline/Models/Timeline.cs +++ b/BackEnd/Timeline/Models/Timeline.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -namespace Timeline.Models +namespace Timeline.Models { public enum TimelineVisibility { diff --git a/BackEnd/Timeline/Services/TimelinePostService.cs b/BackEnd/Timeline/Services/TimelinePostService.cs index 9f0fd550..a8bdbf92 100644 --- a/BackEnd/Timeline/Services/TimelinePostService.cs +++ b/BackEnd/Timeline/Services/TimelinePostService.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Logging; using SixLabors.ImageSharp; using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading.Tasks; using Timeline.Entities; @@ -166,12 +165,12 @@ namespace Timeline.Services if (modifiedSince.HasValue) { - query = query.Include(p => p.Author).Where(p => p.LastUpdated >= modifiedSince || (p.Author != null && p.Author.UsernameChangeTime >= modifiedSince)); + query = query.Where(p => p.LastUpdated >= modifiedSince || (p.Author != null && p.Author.UsernameChangeTime >= modifiedSince)); } query = query.OrderBy(p => p.Time); - return await query.Include(p => p.Author!.Permissions).ToListAsync(); + return await query.ToListAsync(); } public async Task GetPostDataETag(long timelineId, long postId) @@ -270,8 +269,6 @@ namespace Timeline.Services _database.TimelinePosts.Add(postEntity); await _database.SaveChangesAsync(); - await _database.Entry(postEntity).Reference(p => p.Author).Query().Include(a => a.Permissions).LoadAsync(); - return postEntity; } @@ -313,8 +310,6 @@ namespace Timeline.Services _database.TimelinePosts.Add(postEntity); await _database.SaveChangesAsync(); - await _database.Entry(postEntity).Reference(p => p.Author).Query().Include(a => a.Permissions).LoadAsync(); - return postEntity; } diff --git a/BackEnd/Timeline/Services/TimelineService.cs b/BackEnd/Timeline/Services/TimelineService.cs index e310951a..1d1bb320 100644 --- a/BackEnd/Timeline/Services/TimelineService.cs +++ b/BackEnd/Timeline/Services/TimelineService.cs @@ -244,7 +244,7 @@ namespace Timeline.Services public async Task GetTimeline(long id) { - var entity = await _database.Timelines.Where(t => t.Id == id).Include(t => t.Owner).ThenInclude(o => o.Permissions).Include(t => t.Members).ThenInclude(m => m.User).ThenInclude(u => u.Permissions).SingleOrDefaultAsync(); + var entity = await _database.Timelines.Where(t => t.Id == id).SingleOrDefaultAsync(); if (entity is null) throw new TimelineNotExistException(id); @@ -397,7 +397,7 @@ namespace Timeline.Services if (relate == null) { - entities = await ApplyTimelineVisibilityFilter(_database.Timelines).Include(t => t.Owner).ThenInclude(o => o.Permissions).Include(t => t.Members).ThenInclude(m => m.User).ThenInclude(u => u.Permissions).ToListAsync(); + entities = await ApplyTimelineVisibilityFilter(_database.Timelines).ToListAsync(); } else { @@ -405,16 +405,15 @@ namespace Timeline.Services if ((relate.Type & TimelineUserRelationshipType.Own) != 0) { - entities.AddRange(await ApplyTimelineVisibilityFilter(_database.Timelines.Where(t => t.OwnerId == relate.UserId)).Include(t => t.Owner).ThenInclude(o => o.Permissions).Include(t => t.Members).ThenInclude(m => m.User).ThenInclude(u => u.Permissions).ToListAsync()); + entities.AddRange(await ApplyTimelineVisibilityFilter(_database.Timelines.Where(t => t.OwnerId == relate.UserId)).ToListAsync()); } if ((relate.Type & TimelineUserRelationshipType.Join) != 0) { - entities.AddRange(await ApplyTimelineVisibilityFilter(_database.TimelineMembers.Where(m => m.UserId == relate.UserId).Include(m => m.Timeline).ThenInclude(t => t.Members).ThenInclude(m => m.User).ThenInclude(u => u.Permissions).Include(t => t.Timeline.Owner.Permissions).Select(m => m.Timeline)).ToListAsync()); + entities.AddRange(await ApplyTimelineVisibilityFilter(_database.TimelineMembers.Where(m => m.UserId == relate.UserId).Include(m => m.Timeline).Select(m => m.Timeline)).ToListAsync()); } } - return entities; } @@ -435,9 +434,6 @@ namespace Timeline.Services _database.Timelines.Add(entity); await _database.SaveChangesAsync(); - await _database.Entry(entity).Reference(e => e.Owner).Query().Include(o => o.Permissions).LoadAsync(); - await _database.Entry(entity).Collection(e => e.Members).Query().Include(m => m.User).ThenInclude(u => u.Permissions).LoadAsync(); - return entity; } diff --git a/BackEnd/Timeline/Services/UserService.cs b/BackEnd/Timeline/Services/UserService.cs index d341759c..288d208c 100644 --- a/BackEnd/Timeline/Services/UserService.cs +++ b/BackEnd/Timeline/Services/UserService.cs @@ -115,7 +115,7 @@ namespace Timeline.Services public async Task GetUser(long id) { - var user = await _databaseContext.Users.Where(u => u.Id == id).Include(u => u.Permissions).SingleOrDefaultAsync(); + var user = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync(); if (user == null) throw new UserNotExistException(id); @@ -125,7 +125,7 @@ namespace Timeline.Services public async Task> GetUsers() { - return await _databaseContext.Users.Include(u => u.Permissions).ToListAsync(); + return await _databaseContext.Users.ToListAsync(); } public async Task CreateUser(string username, string password) @@ -153,8 +153,6 @@ namespace Timeline.Services _logger.LogInformation(Log.Format(LogDatabaseCreate, ("Id", newEntity.Id), ("Username", username))); - await _databaseContext.Entry(newEntity).Collection(e => e.Permissions).LoadAsync(); - return newEntity; } @@ -172,7 +170,7 @@ namespace Timeline.Services CheckNicknameFormat(param.Nickname, nameof(param)); } - var entity = await _databaseContext.Users.Where(u => u.Id == id).Include(u => u.Permissions).SingleOrDefaultAsync(); + var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync(); if (entity == null) throw new UserNotExistException(id); diff --git a/BackEnd/Timeline/Services/UserTokenManager.cs b/BackEnd/Timeline/Services/UserTokenManager.cs index 4e24c922..78aa0b1f 100644 --- a/BackEnd/Timeline/Services/UserTokenManager.cs +++ b/BackEnd/Timeline/Services/UserTokenManager.cs @@ -3,7 +3,6 @@ using System; using System.Threading.Tasks; using Timeline.Entities; using Timeline.Helpers; -using Timeline.Models; using Timeline.Services.Exceptions; namespace Timeline.Services diff --git a/BackEnd/Timeline/Startup.cs b/BackEnd/Timeline/Startup.cs index a706cf99..70461909 100644 --- a/BackEnd/Timeline/Startup.cs +++ b/BackEnd/Timeline/Startup.cs @@ -20,6 +20,7 @@ using Timeline.Entities; using Timeline.Formatters; using Timeline.Helpers; using Timeline.Models.Converters; +using Timeline.Models.Mapper; using Timeline.Routes; using Timeline.Services; using Timeline.Swagger; @@ -90,6 +91,7 @@ namespace Timeline services.AddSingleton(); services.AddAutoMapper(GetType().Assembly); + services.AddMappers(); services.AddTransient(); -- cgit v1.2.3