diff options
author | crupest <crupest@outlook.com> | 2022-04-09 18:38:46 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2022-04-09 18:38:46 +0800 |
commit | 4db131899145b7aca0ea5fd36984cf1542c9619b (patch) | |
tree | e3b9729dfcc744a79f601b674ec6cecaf47dc9da | |
parent | bab2b1d568a7273b6800dbe6ffd31972d5cedd24 (diff) | |
download | timeline-4db131899145b7aca0ea5fd36984cf1542c9619b.tar.gz timeline-4db131899145b7aca0ea5fd36984cf1542c9619b.tar.bz2 timeline-4db131899145b7aca0ea5fd36984cf1542c9619b.zip |
...
11 files changed, 159 insertions, 33 deletions
diff --git a/BackEnd/Timeline.Tests/IntegratedTests2/HttpClientTestExtensions.cs b/BackEnd/Timeline.Tests/IntegratedTests2/HttpClientTestExtensions.cs index 0124b72a..cd7daf3e 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests2/HttpClientTestExtensions.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests2/HttpClientTestExtensions.cs @@ -1,10 +1,9 @@ -using FluentAssertions;
-using System;
+using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
-using Timeline.Models.Http;
+using FluentAssertions;
using Timeline.Tests.Helpers; namespace Timeline.Tests.IntegratedTests2
diff --git a/BackEnd/Timeline.Tests/IntegratedTests2/IntegratedTestBase.cs b/BackEnd/Timeline.Tests/IntegratedTests2/IntegratedTestBase.cs index 1d01fd0e..24a869a0 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests2/IntegratedTestBase.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests2/IntegratedTestBase.cs @@ -1,9 +1,8 @@ -using Microsoft.AspNetCore.TestHost;
-using Microsoft.Extensions.DependencyInjection;
-using System;
-using System.Collections.Generic;
+using System;
using System.Net.Http;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
using Timeline.Models.Http;
using Timeline.Services.User;
using Timeline.Tests.Helpers;
diff --git a/BackEnd/Timeline.Tests/IntegratedTests2/TimelinePostTest2.cs b/BackEnd/Timeline.Tests/IntegratedTests2/TimelinePostTest2.cs index 5abd969d..9aaae21a 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests2/TimelinePostTest2.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests2/TimelinePostTest2.cs @@ -4,7 +4,6 @@ using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using FluentAssertions; using Timeline.Models; using Timeline.Models.Http; using Xunit; diff --git a/BackEnd/Timeline.Tests/IntegratedTests2/TimelinePostTest3.cs b/BackEnd/Timeline.Tests/IntegratedTests2/TimelinePostTest3.cs index 345a8e81..5313a7c8 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests2/TimelinePostTest3.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests2/TimelinePostTest3.cs @@ -4,7 +4,6 @@ using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using FluentAssertions; using Timeline.Models; using Timeline.Models.Http; using Timeline.Tests.Helpers; diff --git a/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest.cs b/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest.cs index 59b2d225..807314f4 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest.cs @@ -1,9 +1,7 @@ -using System; -using System.Net; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; -using Timeline.Models; using Timeline.Models.Http; using Xunit; using Xunit.Abstractions; diff --git a/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest2.cs b/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest2.cs index c3a19bbb..b5566ba0 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest2.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest2.cs @@ -1,5 +1,4 @@ -using System; -using System.Net; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; diff --git a/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest3.cs b/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest3.cs index 05e01f95..2c8d4c39 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest3.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests2/TimelineTest3.cs @@ -1,9 +1,7 @@ -using System; -using System.Net; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; -using Timeline.Models; using Timeline.Models.Http; using Xunit; using Xunit.Abstractions; diff --git a/BackEnd/Timeline/Models/Page.cs b/BackEnd/Timeline/Models/Page.cs new file mode 100644 index 00000000..807702c1 --- /dev/null +++ b/BackEnd/Timeline/Models/Page.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace Timeline.Models +{ + public class Page<T> + { + public Page() + { + } + + public Page(long pageNumber, long pageSize, long totalCount, List<T> items) + { + PageNumber = pageNumber; + PageSize = pageSize; + TotalPageCount = totalCount / PageSize + (totalCount % PageSize != 0 ? 1 : 0); + TotalCount = totalCount; + Items = items; + } + + public long PageNumber { get; set; } + public long PageSize { get; set; } + public long TotalPageCount { get; set; } + public long TotalCount { get; set; } + public List<T> Items { get; set; } = new List<T>(); + } +} + diff --git a/BackEnd/Timeline/Services/EntityDeletedException.cs b/BackEnd/Timeline/Services/EntityDeletedException.cs new file mode 100644 index 00000000..a31da594 --- /dev/null +++ b/BackEnd/Timeline/Services/EntityDeletedException.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace Timeline.Services +{ + /// <summary> + /// Thrown when an entity is deleted. + /// </summary> + [Serializable] + public class EntityDeletedException : EntityException + { + public EntityDeletedException() : base() { } + public EntityDeletedException(string? message) : base(message) { } + public EntityDeletedException(string? message, Exception? inner) : base(message, inner) { } + public EntityDeletedException(EntityType entityType, IDictionary<string, object> constraints, string? message = null, Exception? inner = null) + : base(entityType, constraints, message ?? Resource.ExceptionEntityNotExist, inner) + { + + } + protected EntityDeletedException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + } +} diff --git a/BackEnd/Timeline/Services/Timeline/ITimelinePostService.cs b/BackEnd/Timeline/Services/Timeline/ITimelinePostService.cs index c0edf857..f595af87 100644 --- a/BackEnd/Timeline/Services/Timeline/ITimelinePostService.cs +++ b/BackEnd/Timeline/Services/Timeline/ITimelinePostService.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Timeline.Entities;
using Timeline.Helpers.Cache;
using Timeline.Models;
-using Timeline.Services.Imaging;
namespace Timeline.Services.Timeline
{
@@ -55,7 +54,29 @@ namespace Timeline.Services.Timeline /// <exception cref="EntityNotExistException">Thrown when timeline does not exist.</exception>
/// <exception cref="EntityNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
/// <exception cref="EntityNotExistException">Thrown when data of that index does not exist.</exception>
- Task<ByteData> GetPostDataAsync(long timelineId, long postId, long dataIndex);
+ Task<ByteData> GetPostDataAsync(long timelineId, long postId, long dataIndex); + + /// <summary> + /// Get posts of a timeline. + /// </summary> + /// <param name="timelineId">The timeline id.</param> + /// <param name="modifiedSince">If not null, only posts modified since (including) the time will be returned.</param> + /// <param name="page">The page to get. Starts from 1.</param> + /// <param name="numberPerPage">Number per page.</param> + /// <returns>A task containing a page of post entity.</returns> + /// <exception cref="EntityNotExistException">Thrown when timeline does not exist.</exception> + Task<Page<TimelinePostEntity>> GetPostsV2Async(long timelineId, DateTime? modifiedSince = null, int? page = null, int? numberPerPage = null);
+
+ /// <summary> + /// Get a post of a timeline. + /// </summary> + /// <param name="timelineId">The timeline id.</param> + /// <param name="postId">The post id.</param> + /// <returns>A task containing a post entity.</returns>
+ /// <exception cref="EntityNotExistException">Thrown when timeline does not exist.</exception>
+ /// <exception cref="EntityNotExistException">Thrown when post does not exist.</exception>
+ /// <exception cref="EntityDeletedException">Thrown when post is deleted.</exception>
+ Task<TimelinePostEntity> GetPostV2Async(long timelineId, long postId);
/// <summary>
/// Create a new post in timeline.
diff --git a/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs b/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs index e6297d2c..40f226ce 100644 --- a/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs +++ b/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs @@ -20,8 +20,8 @@ namespace Timeline.Services.Timeline {
private readonly ILogger<TimelinePostService> _logger;
private readonly DatabaseContext _database;
- private readonly ITimelineService _basicTimelineService;
- private readonly IUserService _basicUserService;
+ private readonly ITimelineService _timelineService;
+ private readonly IUserService _userService;
private readonly IDataManager _dataManager;
private readonly IImageService _imageValidator;
private readonly IClock _clock;
@@ -32,8 +32,8 @@ namespace Timeline.Services.Timeline {
_logger = logger;
_database = database;
- _basicTimelineService = basicTimelineService;
- _basicUserService = basicUserService;
+ _timelineService = basicTimelineService;
+ _userService = basicUserService;
_dataManager = dataManager;
_imageValidator = imageValidator;
_clock = clock;
@@ -53,6 +53,15 @@ namespace Timeline.Services.Timeline ["post-id"] = postId,
["deleted"] = deleted
});
+ } + + private static EntityNotExistException CreatePostDeletedException(long timelineId, long postId)
+ {
+ return new EntityNotExistException(EntityTypes.TimelinePost, new Dictionary<string, object>
+ {
+ ["timeline-id"] = timelineId,
+ ["post-id"] = postId,
+ });
}
private static EntityNotExistException CreatePostDataNotExistException(long timelineId, long postId, long dataIndex)
@@ -73,7 +82,7 @@ namespace Timeline.Services.Timeline throw new ArgumentOutOfRangeException(nameof(numberPerPage), Resource.ExceptionNumberPerPageZeroOrNegative);
- await _basicTimelineService.ThrowIfTimelineNotExist(timelineId);
+ await _timelineService.ThrowIfTimelineNotExist(timelineId);
modifiedSince = modifiedSince?.MyToUtc();
@@ -102,7 +111,7 @@ namespace Timeline.Services.Timeline public async Task<TimelinePostEntity> GetPostAsync(long timelineId, long postId, bool includeDeleted = false)
{
- await _basicTimelineService.ThrowIfTimelineNotExist(timelineId);
+ await _timelineService.ThrowIfTimelineNotExist(timelineId);
var post = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync();
@@ -121,7 +130,7 @@ namespace Timeline.Services.Timeline public async Task<ICacheableDataDigest> GetPostDataDigestAsync(long timelineId, long postId, long dataIndex)
{
- await _basicTimelineService.ThrowIfTimelineNotExist(timelineId);
+ await _timelineService.ThrowIfTimelineNotExist(timelineId);
var postEntity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).Select(p => new { p.Id, p.Deleted }).SingleOrDefaultAsync();
@@ -141,7 +150,7 @@ namespace Timeline.Services.Timeline public async Task<ByteData> GetPostDataAsync(long timelineId, long postId, long dataIndex)
{
- await _basicTimelineService.ThrowIfTimelineNotExist(timelineId);
+ await _timelineService.ThrowIfTimelineNotExist(timelineId);
var postEntity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).Select(p => new { p.Id, p.Deleted }).SingleOrDefaultAsync();
@@ -215,8 +224,8 @@ namespace Timeline.Services.Timeline request.Time = request.Time?.MyToUtc();
- await _basicTimelineService.ThrowIfTimelineNotExist(timelineId);
- await _basicUserService.ThrowIfUserNotExist(authorId);
+ await _timelineService.ThrowIfTimelineNotExist(timelineId);
+ await _userService.ThrowIfUserNotExist(authorId);
var currentTime = _clock.GetCurrentTime();
var finalTime = request.Time ?? currentTime;
@@ -274,7 +283,7 @@ namespace Timeline.Services.Timeline request.Time = request.Time?.MyToUtc();
- await _basicTimelineService.ThrowIfTimelineNotExist(timelineId);
+ await _timelineService.ThrowIfTimelineNotExist(timelineId);
var entity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync();
@@ -309,7 +318,7 @@ namespace Timeline.Services.Timeline public async Task DeletePostAsync(long timelineId, long postId)
{
- await _basicTimelineService.ThrowIfTimelineNotExist(timelineId);
+ await _timelineService.ThrowIfTimelineNotExist(timelineId);
var entity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync();
@@ -351,7 +360,7 @@ namespace Timeline.Services.Timeline public async Task<bool> HasPostModifyPermissionAsync(long timelineId, long postId, long modifierId, bool throwOnPostNotExist = false)
{
- await _basicTimelineService.ThrowIfTimelineNotExist(timelineId);
+ await _timelineService.ThrowIfTimelineNotExist(timelineId);
var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleAsync();
@@ -371,6 +380,59 @@ namespace Timeline.Services.Timeline }
return timelineEntity.OwnerId == modifierId || postEntity.AuthorId == modifierId;
- }
+ } + + public async Task<Page<TimelinePostEntity>> GetPostsV2Async(long timelineId, DateTime? modifiedSince = null, int? page = null, int? numberPerPage = null) + { + if (page.HasValue && page < 0)
+ throw new ArgumentOutOfRangeException(nameof(page), Resource.ExceptionPageNegative);
+ if (numberPerPage.HasValue && numberPerPage <= 0)
+ throw new ArgumentOutOfRangeException(nameof(numberPerPage), Resource.ExceptionNumberPerPageZeroOrNegative);
+
+
+ var timeline = await _timelineService.GetTimelineAsync(timelineId);
+
+ modifiedSince = modifiedSince?.MyToUtc();
+
+ IQueryable<TimelinePostEntity> query = _database.TimelinePosts.Where(p => p.TimelineId == timelineId);
+
+ if (modifiedSince.HasValue)
+ {
+ query = query.Where(p => p.LastUpdated >= modifiedSince || (p.Author != null && p.Author.UsernameChangeTime >= modifiedSince));
+ }
+
+ query = query.OrderBy(p => p.Time); + + var pageNumber = page.GetValueOrDefault(1); + var pageSize = numberPerPage.GetValueOrDefault(20);
+
+ if (pageNumber > 1)
+ {
+ query = query.Skip(pageSize * (pageNumber - 1)).Take(pageSize);
+ }
+
+ var items = await query.ToListAsync(); + + return new Page<TimelinePostEntity>(pageNumber, pageSize, timeline.CurrentPostLocalId, items); + } + + public async Task<TimelinePostEntity> GetPostV2Async(long timelineId, long postId) + { + await _timelineService.ThrowIfTimelineNotExist(timelineId);
+
+ var post = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync();
+
+ if (post is null)
+ {
+ throw CreatePostNotExistException(timelineId, postId, false);
+ }
+
+ if (post.Deleted)
+ {
+ throw CreatePostDeletedException(timelineId, postId);
+ }
+
+ return post; + } }
}
|