From 657fb589137099794e58fbd35beb7d942b376965 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 25 Apr 2021 21:20:04 +0800 Subject: ... --- BackEnd/Timeline/Services/TimelinePostService.cs | 483 ----------------------- 1 file changed, 483 deletions(-) delete mode 100644 BackEnd/Timeline/Services/TimelinePostService.cs (limited to 'BackEnd/Timeline/Services/TimelinePostService.cs') diff --git a/BackEnd/Timeline/Services/TimelinePostService.cs b/BackEnd/Timeline/Services/TimelinePostService.cs deleted file mode 100644 index f64c4c22..00000000 --- a/BackEnd/Timeline/Services/TimelinePostService.cs +++ /dev/null @@ -1,483 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Timeline.Entities; -using Timeline.Helpers; -using Timeline.Helpers.Cache; -using Timeline.Models; -using Timeline.Models.Validation; -using Timeline.Services.Exceptions; - -namespace Timeline.Services -{ - public class TimelinePostCreateRequestData - { - public TimelinePostCreateRequestData(string contentType, byte[] data) - { - ContentType = contentType; - Data = data; - } - - public string ContentType { get; set; } -#pragma warning disable CA1819 // Properties should not return arrays - public byte[] Data { get; set; } -#pragma warning restore CA1819 // Properties should not return arrays - } - - public class TimelinePostCreateRequest - { - public string? Color { get; set; } - - /// If not set, current time is used. - public DateTime? Time { get; set; } - -#pragma warning disable CA2227 - public List DataList { get; set; } = new List(); -#pragma warning restore CA2227 - } - - public class TimelinePostPatchRequest - { - public string? Color { get; set; } - public DateTime? Time { get; set; } - } - - public interface ITimelinePostService - { - /// - /// Get all the posts in the timeline. - /// - /// The id of the timeline. - /// The time that posts have been modified since. - /// Whether include deleted posts. - /// A list of all posts. - /// Thrown when timeline does not exist. - Task> GetPosts(long timelineId, DateTime? modifiedSince = null, bool includeDeleted = false); - - /// - /// Get a post of a timeline. - /// - /// The id of the timeline of the post. - /// The id of the post. - /// If true, return the entity even if it is deleted. - /// The post. - /// Thrown when timeline does not exist. - /// Thrown when post of does not exist or has been deleted. - Task GetPost(long timelineId, long postId, bool includeDeleted = false); - - /// - /// Get the data digest of a post. - /// - /// The timeline id. - /// The post id. - /// The index of the data. - /// The data digest. - /// Thrown when timeline does not exist. - /// Thrown when post of does not exist or has been deleted. - /// Thrown when data of that index does not exist. - Task GetPostDataDigest(long timelineId, long postId, long dataIndex); - - /// - /// Get the data of a post. - /// - /// The timeline id. - /// The post id. - /// The index of the data. - /// The data. - /// Thrown when timeline does not exist. - /// Thrown when post of does not exist or has been deleted. - /// Thrown when data of that index does not exist. - Task GetPostData(long timelineId, long postId, long dataIndex); - - /// - /// Create a new post in timeline. - /// - /// The id of the timeline to create post against. - /// The author's user id. - /// Info about the post. - /// The entity of the created post. - /// Thrown when is null. - /// Thrown when is of invalid format. - /// Thrown when timeline does not exist. - /// Thrown if user of does not exist. - /// Thrown if data is not a image. Validated by . - Task CreatePost(long timelineId, long authorId, TimelinePostCreateRequest request); - - /// - /// Modify a post. Change its properties or replace its content. - /// - /// The timeline id. - /// The post id. - /// The request. - /// The entity of the patched post. - /// Thrown when is null. - /// Thrown when is of invalid format. - /// Thrown when timeline does not exist. - /// Thrown when post does not exist. - Task PatchPost(long timelineId, long postId, TimelinePostPatchRequest request); - - /// - /// Delete a post. - /// - /// The id of the timeline to delete post against. - /// The id of the post to delete. - /// Thrown when timeline does not exist. - /// Thrown when the post with given id does not exist or is deleted already. - /// - /// First use to check the permission. - /// - Task DeletePost(long timelineId, long postId); - - /// - /// Delete all posts of the given user. Used when delete a user. - /// - /// The id of the user. - Task DeleteAllPostsOfUser(long userId); - - /// - /// Verify whether a user has the permission to modify a post. - /// - /// The id of the timeline. - /// The id of the post. - /// The id of the user to check on. - /// True if you want it to throw . Default false. - /// True if can modify, false if can't modify. - /// Thrown when timeline does not exist. - /// Thrown when the post with given id does not exist or is deleted already and is true. - /// - /// Unless is true, this method should return true if the post does not exist. - /// If the post is deleted, its author info still exists, so it is checked as the post is not deleted unless is true. - /// This method does not check whether the user is administrator. - /// It only checks whether he is the author of the post or the owner of the timeline. - /// Return false when user with modifier id does not exist. - /// - Task HasPostModifyPermission(long timelineId, long postId, long modifierId, bool throwOnPostNotExist = false); - } - - public class TimelinePostService : ITimelinePostService - { - private readonly ILogger _logger; - private readonly DatabaseContext _database; - private readonly IBasicTimelineService _basicTimelineService; - private readonly IBasicUserService _basicUserService; - private readonly IDataManager _dataManager; - private readonly IImageValidator _imageValidator; - private readonly IClock _clock; - private readonly ColorValidator _colorValidator = new ColorValidator(); - - public TimelinePostService(ILogger logger, DatabaseContext database, IBasicTimelineService basicTimelineService, IBasicUserService basicUserService, IDataManager dataManager, IImageValidator imageValidator, IClock clock) - { - _logger = logger; - _database = database; - _basicTimelineService = basicTimelineService; - _basicUserService = basicUserService; - _dataManager = dataManager; - _imageValidator = imageValidator; - _clock = clock; - } - - private async Task CheckTimelineExistence(long timelineId) - { - if (!await _basicTimelineService.CheckExistence(timelineId)) - throw new TimelineNotExistException(timelineId); - } - - private async Task CheckUserExistence(long userId) - { - if (!await _basicUserService.CheckUserExistence(userId)) - throw new UserNotExistException(userId); - } - - public async Task> GetPosts(long timelineId, DateTime? modifiedSince = null, bool includeDeleted = false) - { - await CheckTimelineExistence(timelineId); - - modifiedSince = modifiedSince?.MyToUtc(); - - IQueryable query = _database.TimelinePosts.Where(p => p.TimelineId == timelineId); - - if (!includeDeleted) - { - query = query.Where(p => !p.Deleted); - } - - if (modifiedSince.HasValue) - { - query = query.Where(p => p.LastUpdated >= modifiedSince || (p.Author != null && p.Author.UsernameChangeTime >= modifiedSince)); - } - - query = query.OrderBy(p => p.Time); - - return await query.ToListAsync(); - } - - public async Task GetPost(long timelineId, long postId, bool includeDeleted = false) - { - await CheckTimelineExistence(timelineId); - - var post = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync(); - - if (post is null) - { - throw new TimelinePostNotExistException(timelineId, postId, false); - } - - if (!includeDeleted && post.Deleted) - { - throw new TimelinePostNotExistException(timelineId, postId, true); - } - - return post; - } - - public async Task GetPostDataDigest(long timelineId, long postId, long dataIndex) - { - await CheckTimelineExistence(timelineId); - - var postEntity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).Select(p => new { p.Id, p.Deleted }).SingleOrDefaultAsync(); - - if (postEntity is null) - throw new TimelinePostNotExistException(timelineId, postId, false); - - if (postEntity.Deleted) - throw new TimelinePostNotExistException(timelineId, postId, true); - - var dataEntity = await _database.TimelinePostData.Where(d => d.PostId == postEntity.Id && d.Index == dataIndex).SingleOrDefaultAsync(); - - if (dataEntity is null) - throw new TimelinePostDataNotExistException(timelineId, postId, dataIndex); - - return new CacheableDataDigest(dataEntity.DataTag, dataEntity.LastUpdated); - } - - public async Task GetPostData(long timelineId, long postId, long dataIndex) - { - await CheckTimelineExistence(timelineId); - - var postEntity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).Select(p => new { p.Id, p.Deleted }).SingleOrDefaultAsync(); - - if (postEntity is null) - throw new TimelinePostNotExistException(timelineId, postId, false); - - if (postEntity.Deleted) - throw new TimelinePostNotExistException(timelineId, postId, true); - - var dataEntity = await _database.TimelinePostData.Where(d => d.PostId == postEntity.Id && d.Index == dataIndex).SingleOrDefaultAsync(); - - if (dataEntity is null) - throw new TimelinePostDataNotExistException(timelineId, postId, dataIndex); - - var data = await _dataManager.GetEntryAndCheck(dataEntity.DataTag, $"Timeline {timelineId}, post {postId}, data {dataIndex} requires this data."); - - return new ByteData(data, dataEntity.Kind); - } - - public async Task CreatePost(long timelineId, long authorId, TimelinePostCreateRequest request) - { - if (request is null) - throw new ArgumentNullException(nameof(request)); - - { - if (!_colorValidator.Validate(request.Color, out var message)) - throw new ArgumentException("Color is not valid.", nameof(request)); - } - - if (request.DataList is null) - throw new ArgumentException("Data list can't be null.", nameof(request)); - - if (request.DataList.Count == 0) - throw new ArgumentException("Data list can't be empty.", nameof(request)); - - if (request.DataList.Count > 100) - throw new ArgumentException("Data list count can't be bigger than 100.", nameof(request)); - - for (int index = 0; index < request.DataList.Count; index++) - { - var data = request.DataList[index]; - - switch (data.ContentType) - { - case MimeTypes.ImageGif: - case MimeTypes.ImageJpeg: - case MimeTypes.ImagePng: - case MimeTypes.ImageWebp: - try - { - await _imageValidator.Validate(data.Data, data.ContentType); - } - catch (ImageException e) - { - throw new TimelinePostCreateDataException(index, "Image validation failed.", e); - } - break; - case MimeTypes.TextPlain: - case MimeTypes.TextMarkdown: - try - { - new UTF8Encoding(false, true).GetString(data.Data); - } - catch (DecoderFallbackException e) - { - throw new TimelinePostCreateDataException(index, "Text is not a valid utf-8 sequence.", e); - } - break; - default: - throw new TimelinePostCreateDataException(index, "Unsupported content type."); - } - } - - request.Time = request.Time?.MyToUtc(); - - await CheckTimelineExistence(timelineId); - await CheckUserExistence(authorId); - - var currentTime = _clock.GetCurrentTime(); - var finalTime = request.Time ?? currentTime; - - await using var transaction = await _database.Database.BeginTransactionAsync(); - - var postEntity = new TimelinePostEntity - { - AuthorId = authorId, - TimelineId = timelineId, - Time = finalTime, - LastUpdated = currentTime, - Color = request.Color - }; - - var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).SingleAsync(); - timelineEntity.CurrentPostLocalId += 1; - postEntity.LocalId = timelineEntity.CurrentPostLocalId; - _database.TimelinePosts.Add(postEntity); - await _database.SaveChangesAsync(); - - List dataTags = new List(); - - for (int index = 0; index < request.DataList.Count; index++) - { - var data = request.DataList[index]; - - var tag = await _dataManager.RetainEntry(data.Data); - - _database.TimelinePostData.Add(new TimelinePostDataEntity - { - DataTag = tag, - Kind = data.ContentType, - Index = index, - PostId = postEntity.Id, - LastUpdated = currentTime, - }); - } - - await _database.SaveChangesAsync(); - - await transaction.CommitAsync(); - - return postEntity; - } - - public async Task PatchPost(long timelineId, long postId, TimelinePostPatchRequest request) - { - if (request is null) - throw new ArgumentNullException(nameof(request)); - - { - if (!_colorValidator.Validate(request.Color, out var message)) - throw new ArgumentException("Color is not valid.", nameof(request)); - } - - request.Time = request.Time?.MyToUtc(); - - await CheckTimelineExistence(timelineId); - - var entity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync(); - - if (entity is null) - throw new TimelinePostNotExistException(timelineId, postId, false); - - if (entity.Deleted) - throw new TimelinePostNotExistException(timelineId, postId, true); - - if (request.Time.HasValue) - entity.Time = request.Time.Value; - - if (request.Color is not null) - entity.Color = request.Color; - - entity.LastUpdated = _clock.GetCurrentTime(); - - await _database.SaveChangesAsync(); - - return entity; - } - - public async Task DeletePost(long timelineId, long postId) - { - await CheckTimelineExistence(timelineId); - - var entity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync(); - - if (entity == null) - throw new TimelinePostNotExistException(timelineId, postId, false); - - if (entity.Deleted) - throw new TimelinePostNotExistException(timelineId, postId, true); - - await using var transaction = await _database.Database.BeginTransactionAsync(); - - entity.Deleted = true; - entity.LastUpdated = _clock.GetCurrentTime(); - - var dataEntities = await _database.TimelinePostData.Where(d => d.PostId == entity.Id).ToListAsync(); - - foreach (var dataEntity in dataEntities) - { - await _dataManager.FreeEntry(dataEntity.DataTag); - } - - _database.TimelinePostData.RemoveRange(dataEntities); - - await _database.SaveChangesAsync(); - - await transaction.CommitAsync(); - } - - public async Task DeleteAllPostsOfUser(long userId) - { - var postEntities = await _database.TimelinePosts.Where(p => p.AuthorId == userId).Select(p => new { p.TimelineId, p.LocalId }).ToListAsync(); - - foreach (var postEntity in postEntities) - { - await this.DeletePost(postEntity.TimelineId, postEntity.LocalId); - } - } - - public async Task HasPostModifyPermission(long timelineId, long postId, long modifierId, bool throwOnPostNotExist = false) - { - await CheckTimelineExistence(timelineId); - - var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleAsync(); - - var postEntity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).Select(p => new { p.Deleted, p.AuthorId }).SingleOrDefaultAsync(); - - if (postEntity is null) - { - if (throwOnPostNotExist) - throw new TimelinePostNotExistException(timelineId, postId, false); - else - return true; - } - - if (postEntity.Deleted && throwOnPostNotExist) - { - throw new TimelinePostNotExistException(timelineId, postId, true); - } - - return timelineEntity.OwnerId == modifierId || postEntity.AuthorId == modifierId; - } - } -} -- cgit v1.2.3