aboutsummaryrefslogtreecommitdiff
path: root/BackEnd/Timeline
diff options
context:
space:
mode:
Diffstat (limited to 'BackEnd/Timeline')
-rw-r--r--BackEnd/Timeline/Models/Page.cs28
-rw-r--r--BackEnd/Timeline/Services/EntityDeletedException.cs24
-rw-r--r--BackEnd/Timeline/Services/Timeline/ITimelinePostService.cs25
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelinePostService.cs90
4 files changed, 151 insertions, 16 deletions
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;
+ }
}
}