aboutsummaryrefslogtreecommitdiff
path: root/BackEnd/Timeline/Services
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2021-04-28 17:16:45 +0800
committercrupest <crupest@outlook.com>2021-04-28 17:16:45 +0800
commit76ea7c9f7295f1507bc87154b36cc0f2ea22036e (patch)
treeed6e2b0e7dcad128bf9b971e74c233d23113e284 /BackEnd/Timeline/Services
parent344d189e5860a20ebe42cec03b86974a2a3aaa95 (diff)
downloadtimeline-76ea7c9f7295f1507bc87154b36cc0f2ea22036e.tar.gz
timeline-76ea7c9f7295f1507bc87154b36cc0f2ea22036e.tar.bz2
timeline-76ea7c9f7295f1507bc87154b36cc0f2ea22036e.zip
...
Diffstat (limited to 'BackEnd/Timeline/Services')
-rw-r--r--BackEnd/Timeline/Services/Api/BookmarkTimelineService.cs8
-rw-r--r--BackEnd/Timeline/Services/Api/HighlightTimelineService.cs8
-rw-r--r--BackEnd/Timeline/Services/Mapper/TimelineMapper.cs6
-rw-r--r--BackEnd/Timeline/Services/Timeline/BasicTimelineService.cs57
-rw-r--r--BackEnd/Timeline/Services/Timeline/IBasicTimelineService.cs36
-rw-r--r--BackEnd/Timeline/Services/Timeline/ITimelinePostService.cs123
-rw-r--r--BackEnd/Timeline/Services/Timeline/ITimelineService.cs122
-rw-r--r--BackEnd/Timeline/Services/Timeline/Resource.Designer.cs18
-rw-r--r--BackEnd/Timeline/Services/Timeline/Resource.resx6
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelineChangePropertyParams.cs13
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelineHelper.cs21
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelinePostCreateRequest.cs17
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelinePostCreateRequestData.cs16
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelinePostPatchRequest.cs10
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelinePostService.cs166
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelineService.cs213
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelineServiceExtensions.cs19
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelineUserRelationship.cs14
-rw-r--r--BackEnd/Timeline/Services/Timeline/TimelineUserRelationshipType.cs9
-rw-r--r--BackEnd/Timeline/Services/Token/IUserTokenManager.cs4
-rw-r--r--BackEnd/Timeline/Services/Token/UserTokenManager.cs4
-rw-r--r--BackEnd/Timeline/Services/User/UserDeleteService.cs2
22 files changed, 494 insertions, 398 deletions
diff --git a/BackEnd/Timeline/Services/Api/BookmarkTimelineService.cs b/BackEnd/Timeline/Services/Api/BookmarkTimelineService.cs
index cabc1db2..37b55199 100644
--- a/BackEnd/Timeline/Services/Api/BookmarkTimelineService.cs
+++ b/BackEnd/Timeline/Services/Api/BookmarkTimelineService.cs
@@ -96,7 +96,7 @@ namespace Timeline.Services.Api
if (!await _userService.CheckUserExistenceAsync(userId))
throw new UserNotExistException(userId);
- if (!await _timelineService.CheckExistence(timelineId))
+ if (!await _timelineService.CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
if (await _database.BookmarkTimelines.AnyAsync(t => t.TimelineId == timelineId && t.UserId == userId))
@@ -128,7 +128,7 @@ namespace Timeline.Services.Api
if (checkUserExistence && !await _userService.CheckUserExistenceAsync(userId))
throw new UserNotExistException(userId);
- if (checkTimelineExistence && !await _timelineService.CheckExistence(timelineId))
+ if (checkTimelineExistence && !await _timelineService.CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
return await _database.BookmarkTimelines.AnyAsync(b => b.TimelineId == timelineId && b.UserId == userId);
@@ -139,7 +139,7 @@ namespace Timeline.Services.Api
if (!await _userService.CheckUserExistenceAsync(userId))
throw new UserNotExistException(userId);
- if (!await _timelineService.CheckExistence(timelineId))
+ if (!await _timelineService.CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
var entity = await _database.BookmarkTimelines.SingleOrDefaultAsync(t => t.TimelineId == timelineId && t.UserId == userId);
@@ -181,7 +181,7 @@ namespace Timeline.Services.Api
if (!await _userService.CheckUserExistenceAsync(userId))
throw new UserNotExistException(userId);
- if (!await _timelineService.CheckExistence(timelineId))
+ if (!await _timelineService.CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
var entity = await _database.BookmarkTimelines.SingleOrDefaultAsync(t => t.UserId == userId && t.TimelineId == timelineId);
diff --git a/BackEnd/Timeline/Services/Api/HighlightTimelineService.cs b/BackEnd/Timeline/Services/Api/HighlightTimelineService.cs
index 419aa68d..8224f1fe 100644
--- a/BackEnd/Timeline/Services/Api/HighlightTimelineService.cs
+++ b/BackEnd/Timeline/Services/Api/HighlightTimelineService.cs
@@ -92,7 +92,7 @@ namespace Timeline.Services.Api
public async Task<bool> AddHighlightTimeline(long timelineId, long? operatorId)
{
- if (!await _timelineService.CheckExistence(timelineId))
+ if (!await _timelineService.CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
if (operatorId.HasValue && !await _userService.CheckUserExistenceAsync(operatorId.Value))
@@ -118,7 +118,7 @@ namespace Timeline.Services.Api
public async Task<bool> RemoveHighlightTimeline(long timelineId, long? operatorId)
{
- if (!await _timelineService.CheckExistence(timelineId))
+ if (!await _timelineService.CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
if (operatorId.HasValue && !await _userService.CheckUserExistenceAsync(operatorId.Value))
@@ -146,7 +146,7 @@ namespace Timeline.Services.Api
public async Task MoveHighlightTimeline(long timelineId, long newPosition)
{
- if (!await _timelineService.CheckExistence(timelineId))
+ if (!await _timelineService.CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
var entity = await _database.HighlightTimelines.SingleOrDefaultAsync(t => t.TimelineId == timelineId);
@@ -185,7 +185,7 @@ namespace Timeline.Services.Api
public async Task<bool> IsHighlightTimeline(long timelineId, bool checkTimelineExistence = true)
{
- if (checkTimelineExistence && !await _timelineService.CheckExistence(timelineId))
+ if (checkTimelineExistence && !await _timelineService.CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
return await _database.HighlightTimelines.AnyAsync(t => t.TimelineId == timelineId);
diff --git a/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs b/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs
index 5d823a04..c8279b42 100644
--- a/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs
+++ b/BackEnd/Timeline/Services/Mapper/TimelineMapper.cs
@@ -49,7 +49,7 @@ namespace Timeline.Services.Mapper
}
else
{
- manageable = await _timelineService.HasManagePermission(entity.Id, userId.Value);
+ manageable = await _timelineService.HasManagePermissionAsync(entity.Id, userId.Value);
}
bool postable;
@@ -59,7 +59,7 @@ namespace Timeline.Services.Mapper
}
else
{
- postable = await _timelineService.IsMemberOf(entity.Id, userId.Value);
+ postable = await _timelineService.IsMemberOfAsync(entity.Id, userId.Value);
}
return new HttpTimeline(
@@ -123,7 +123,7 @@ namespace Timeline.Services.Mapper
}
else
{
- editable = await _timelinePostService.HasPostModifyPermission(entity.TimelineId, entity.LocalId, userId.Value);
+ editable = await _timelinePostService.HasPostModifyPermissionAsync(entity.TimelineId, entity.LocalId, userId.Value);
}
diff --git a/BackEnd/Timeline/Services/Timeline/BasicTimelineService.cs b/BackEnd/Timeline/Services/Timeline/BasicTimelineService.cs
index d633be4d..7476a860 100644
--- a/BackEnd/Timeline/Services/Timeline/BasicTimelineService.cs
+++ b/BackEnd/Timeline/Services/Timeline/BasicTimelineService.cs
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -10,39 +11,10 @@ using Timeline.Services.User;
namespace Timeline.Services.Timeline
{
- /// <summary>
- /// This service provide some basic timeline functions, which should be used internally for other services.
- /// </summary>
- public interface IBasicTimelineService
- {
- /// <summary>
- /// Check whether a timeline with given id exists without getting full info.
- /// </summary>
- /// <param name="id">The timeline id.</param>
- /// <returns>True if exist. Otherwise false.</returns>
- Task<bool> CheckExistence(long id);
-
- /// <summary>
- /// Get the timeline id by name.
- /// </summary>
- /// <param name="timelineName">Timeline name.</param>
- /// <returns>Id of the timeline.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <remarks>
- /// If name is of personal timeline and the timeline does not exist, it will be created if user exists.
- /// If the user does not exist, <see cref="TimelineNotExistException"/> will be thrown with <see cref="UserNotExistException"/> as inner exception.
- ///</remarks>
- Task<long> GetTimelineIdByName(string timelineName);
- }
-
-
public class BasicTimelineService : IBasicTimelineService
{
+ private readonly ILogger<BasicTimelineService> _logger;
+
private readonly DatabaseContext _database;
private readonly IBasicUserService _basicUserService;
@@ -50,8 +22,9 @@ namespace Timeline.Services.Timeline
private readonly GeneralTimelineNameValidator _generalTimelineNameValidator = new GeneralTimelineNameValidator();
- public BasicTimelineService(DatabaseContext database, IBasicUserService basicUserService, IClock clock)
+ public BasicTimelineService(ILoggerFactory loggerFactory, DatabaseContext database, IBasicUserService basicUserService, IClock clock)
{
+ _logger = loggerFactory.CreateLogger<BasicTimelineService>();
_database = database;
_basicUserService = basicUserService;
_clock = clock;
@@ -74,27 +47,33 @@ namespace Timeline.Services.Timeline
};
}
- public async Task<bool> CheckExistence(long id)
+ protected void CheckGeneralTimelineName(string timelineName, string? paramName)
+ {
+ if (!_generalTimelineNameValidator.Validate(timelineName, out var message))
+ throw new ArgumentException(string.Format(Resource.ExceptionGeneralTimelineNameBadFormat, message), paramName);
+ }
+
+ public async Task<bool> CheckTimelineExistenceAsync(long id)
{
return await _database.Timelines.AnyAsync(t => t.Id == id);
}
- public async Task<long> GetTimelineIdByName(string timelineName)
+ public async Task<long> GetTimelineIdByNameAsync(string timelineName)
{
if (timelineName == null)
throw new ArgumentNullException(nameof(timelineName));
- if (!_generalTimelineNameValidator.Validate(timelineName, out var message))
- throw new ArgumentException(message);
+ CheckGeneralTimelineName(timelineName, nameof(timelineName));
- timelineName = TimelineHelper.ExtractTimelineName(timelineName, out var isPersonal);
+ var name = TimelineHelper.ExtractTimelineName(timelineName, out var isPersonal);
if (isPersonal)
{
+ var username = name;
long userId;
try
{
- userId = await _basicUserService.GetUserIdByUsernameAsync(timelineName);
+ userId = await _basicUserService.GetUserIdByUsernameAsync(username);
}
catch (UserNotExistException e)
{
@@ -113,6 +92,8 @@ namespace Timeline.Services.Timeline
_database.Timelines.Add(newTimelineEntity);
await _database.SaveChangesAsync();
+ _logger.LogInformation(Resource.LogPersonalTimelineAutoCreate, username);
+
return newTimelineEntity.Id;
}
}
diff --git a/BackEnd/Timeline/Services/Timeline/IBasicTimelineService.cs b/BackEnd/Timeline/Services/Timeline/IBasicTimelineService.cs
new file mode 100644
index 00000000..b32fa3f7
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/IBasicTimelineService.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Threading.Tasks;
+using Timeline.Services.User;
+
+namespace Timeline.Services.Timeline
+{
+ /// <summary>
+ /// This service provide some basic timeline functions, which should be used internally for other services.
+ /// </summary>
+ public interface IBasicTimelineService
+ {
+ /// <summary>
+ /// Check whether a timeline with given id exists without getting full info.
+ /// </summary>
+ /// <param name="id">The timeline id.</param>
+ /// <returns>True if exist. Otherwise false.</returns>
+ Task<bool> CheckTimelineExistenceAsync(long id);
+
+ /// <summary>
+ /// Get the timeline id by name.
+ /// </summary>
+ /// <param name="timelineName">Timeline name.</param>
+ /// <returns>Id of the timeline.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
+ /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
+ /// <exception cref="TimelineNotExistException">
+ /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
+ /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
+ /// </exception>
+ /// <remarks>
+ /// If name is of personal timeline and the timeline does not exist, it will be created if user exists.
+ /// If the user does not exist, <see cref="TimelineNotExistException"/> will be thrown with <see cref="UserNotExistException"/> as inner exception.
+ ///</remarks>
+ Task<long> GetTimelineIdByNameAsync(string timelineName);
+ }
+}
diff --git a/BackEnd/Timeline/Services/Timeline/ITimelinePostService.cs b/BackEnd/Timeline/Services/Timeline/ITimelinePostService.cs
new file mode 100644
index 00000000..50af9fc2
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/ITimelinePostService.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Timeline.Entities;
+using Timeline.Helpers.Cache;
+using Timeline.Models;
+using Timeline.Services.Imaging;
+using Timeline.Services.User;
+
+namespace Timeline.Services.Timeline
+{
+ public interface ITimelinePostService
+ {
+ /// <summary>
+ /// Get all the posts in the timeline.
+ /// </summary>
+ /// <param name="timelineId">The id of the timeline.</param>
+ /// <param name="modifiedSince">The time that posts have been modified since.</param>
+ /// <param name="includeDeleted">Whether include deleted posts.</param>
+ /// <returns>A list of all posts.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ Task<List<TimelinePostEntity>> GetPostsAsync(long timelineId, DateTime? modifiedSince = null, bool includeDeleted = false);
+
+ /// <summary>
+ /// Get a post of a timeline.
+ /// </summary>
+ /// <param name="timelineId">The id of the timeline of the post.</param>
+ /// <param name="postId">The id of the post.</param>
+ /// <param name="includeDeleted">If true, return the entity even if it is deleted.</param>
+ /// <returns>The post.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <exception cref="TimelinePostNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
+ Task<TimelinePostEntity> GetPostAsync(long timelineId, long postId, bool includeDeleted = false);
+
+ /// <summary>
+ /// Get the data digest of a post.
+ /// </summary>
+ /// <param name="timelineId">The timeline id.</param>
+ /// <param name="postId">The post id.</param>
+ /// <param name="dataIndex">The index of the data.</param>
+ /// <returns>The data digest.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <exception cref="TimelinePostNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
+ /// <exception cref="TimelinePostDataNotExistException">Thrown when data of that index does not exist.</exception>
+ Task<ICacheableDataDigest> GetPostDataDigestAsync(long timelineId, long postId, long dataIndex);
+
+ /// <summary>
+ /// Get the data of a post.
+ /// </summary>
+ /// <param name="timelineId">The timeline id.</param>
+ /// <param name="postId">The post id.</param>
+ /// <param name="dataIndex">The index of the data.</param>
+ /// <returns>The data.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <exception cref="TimelinePostNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
+ /// <exception cref="TimelinePostDataNotExistException">Thrown when data of that index does not exist.</exception>
+ Task<ByteData> GetPostDataAsync(long timelineId, long postId, long dataIndex);
+
+ /// <summary>
+ /// Create a new post in timeline.
+ /// </summary>
+ /// <param name="timelineId">The id of the timeline to create post against.</param>
+ /// <param name="authorId">The author's user id.</param>
+ /// <param name="request">Info about the post.</param>
+ /// <returns>The entity of the created post.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="request"/> is of invalid format.</exception>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <exception cref="UserNotExistException">Thrown if user of <paramref name="authorId"/> does not exist.</exception>
+ /// <exception cref="ImageException">Thrown if data is not a image. Validated by <see cref="ImageService"/>.</exception>
+ Task<TimelinePostEntity> CreatePostAsync(long timelineId, long authorId, TimelinePostCreateRequest request);
+
+ /// <summary>
+ /// Modify a post. Change its properties or replace its content.
+ /// </summary>
+ /// <param name="timelineId">The timeline id.</param>
+ /// <param name="postId">The post id.</param>
+ /// <param name="request">The request.</param>
+ /// <returns>The entity of the patched post.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="request"/> is of invalid format.</exception>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <exception cref="TimelinePostNotExistException">Thrown when post does not exist.</exception>
+ Task<TimelinePostEntity> PatchPostAsync(long timelineId, long postId, TimelinePostPatchRequest request);
+
+ /// <summary>
+ /// Delete a post.
+ /// </summary>
+ /// <param name="timelineId">The id of the timeline to delete post against.</param>
+ /// <param name="postId">The id of the post to delete.</param>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <exception cref="TimelinePostNotExistException">Thrown when the post with given id does not exist or is deleted already.</exception>
+ /// <remarks>
+ /// First use <see cref="HasPostModifyPermissionAsync(long, long, long, bool)"/> to check the permission.
+ /// </remarks>
+ Task DeletePostAsync(long timelineId, long postId);
+
+ /// <summary>
+ /// Delete all posts of the given user. Used when delete a user.
+ /// </summary>
+ /// <param name="userId">The id of the user.</param>
+ Task DeleteAllPostsOfUserAsync(long userId);
+
+ /// <summary>
+ /// Verify whether a user has the permission to modify a post.
+ /// </summary>
+ /// <param name="timelineId">The id of the timeline.</param>
+ /// <param name="postId">The id of the post.</param>
+ /// <param name="modifierId">The id of the user to check on.</param>
+ /// <param name="throwOnPostNotExist">True if you want it to throw <see cref="TimelinePostNotExistException"/>. Default false.</param>
+ /// <returns>True if can modify, false if can't modify.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <exception cref="TimelinePostNotExistException">Thrown when the post with given id does not exist or is deleted already and <paramref name="throwOnPostNotExist"/> is true.</exception>
+ /// <remarks>
+ /// Unless <paramref name="throwOnPostNotExist"/> 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 <paramref name="throwOnPostNotExist"/> 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.
+ /// </remarks>
+ Task<bool> HasPostModifyPermissionAsync(long timelineId, long postId, long modifierId, bool throwOnPostNotExist = false);
+ }
+}
diff --git a/BackEnd/Timeline/Services/Timeline/ITimelineService.cs b/BackEnd/Timeline/Services/Timeline/ITimelineService.cs
new file mode 100644
index 00000000..9c313b0b
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/ITimelineService.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Timeline.Entities;
+using Timeline.Models;
+using Timeline.Services.User;
+
+namespace Timeline.Services.Timeline
+{
+ /// <summary>
+ /// This define the interface of both personal timeline and ordinary timeline.
+ /// </summary>
+ public interface ITimelineService : IBasicTimelineService
+ {
+ /// <summary>
+ /// Get the timeline info.
+ /// </summary>
+ /// <param name="id">Id of timeline.</param>
+ /// <returns>The timeline info.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ Task<TimelineEntity> GetTimelineAsync(long id);
+
+ /// <summary>
+ /// Set the properties of a timeline.
+ /// </summary>
+ /// <param name="id">The id of the timeline.</param>
+ /// <param name="newProperties">The new properties. Null member means not to change.</param>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="newProperties"/> is null.</exception>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline with given id does not exist.</exception>
+ /// <exception cref="EntityAlreadyExistException">Thrown when a timeline with new name already exists.</exception>
+ Task ChangePropertyAsync(long id, TimelineChangePropertyParams newProperties);
+
+ /// <summary>
+ /// Add a member to timeline.
+ /// </summary>
+ /// <param name="timelineId">Timeline id.</param>
+ /// <param name="userId">User id.</param>
+ /// <returns>True if the memeber was added. False if it is already a member.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <exception cref="UserNotExistException">Thrown when the user does not exist.</exception>
+ Task<bool> AddMemberAsync(long timelineId, long userId);
+
+ /// <summary>
+ /// Remove a member from timeline.
+ /// </summary>
+ /// <param name="timelineId">Timeline id.</param>
+ /// <param name="userId">User id.</param>
+ /// <returns>True if the memeber was removed. False if it was not a member before.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <exception cref="UserNotExistException">Thrown when the user does not exist.</exception>
+ Task<bool> RemoveMemberAsync(long timelineId, long userId);
+
+ /// <summary>
+ /// Check whether a user can manage(change timeline info, member, ...) a timeline.
+ /// </summary>
+ /// <param name="timelineId">The id of the timeline.</param>
+ /// <param name="userId">The id of the user to check on.</param>
+ /// <returns>True if the user can manage the timeline, otherwise false.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <remarks>
+ /// This method does not check whether visitor is administrator.
+ /// Return false if user with user id does not exist.
+ /// </remarks>
+ Task<bool> HasManagePermissionAsync(long timelineId, long userId);
+
+ /// <summary>
+ /// Verify whether a visitor has the permission to read a timeline.
+ /// </summary>
+ /// <param name="timelineId">The id of the timeline.</param>
+ /// <param name="visitorId">The id of the user to check on. Null means visitor without account.</param>
+ /// <returns>True if can read, false if can't read.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <remarks>
+ /// This method does not check whether visitor is administrator.
+ /// Return false if user with visitor id does not exist.
+ /// </remarks>
+ Task<bool> HasReadPermissionAsync(long timelineId, long? visitorId);
+
+ /// <summary>
+ /// Verify whether a user is member of a timeline.
+ /// </summary>
+ /// <param name="timelineId">The id of the timeline.</param>
+ /// <param name="userId">The id of user to check on.</param>
+ /// <returns>True if it is a member, false if not.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
+ /// <remarks>
+ /// Timeline owner is also considered as a member.
+ /// Return false when user with user id does not exist.
+ /// </remarks>
+ Task<bool> IsMemberOfAsync(long timelineId, long userId);
+
+ /// <summary>
+ /// Get all timelines including personal and ordinary timelines.
+ /// </summary>
+ /// <param name="relate">Filter timelines related (own or is a member) to specific user.</param>
+ /// <param name="visibility">Filter timelines with given visibility. If null or empty, all visibilities are returned. Duplicate value are ignored.</param>
+ /// <returns>The list of timelines.</returns>
+ /// <remarks>
+ /// If user with related user id does not exist, empty list will be returned.
+ /// </remarks>
+ Task<List<TimelineEntity>> GetTimelinesAsync(TimelineUserRelationship? relate = null, List<TimelineVisibility>? visibility = null);
+
+ /// <summary>
+ /// Create a timeline.
+ /// </summary>
+ /// <param name="timelineName">The name of the timeline.</param>
+ /// <param name="ownerId">The id of owner of the timeline.</param>
+ /// <returns>The info of the new timeline.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when timeline name is invalid.</exception>
+ /// <exception cref="EntityAlreadyExistException">Thrown when the timeline already exists.</exception>
+ /// <exception cref="UserNotExistException">Thrown when the owner user does not exist.</exception>
+ Task<TimelineEntity> CreateTimelineAsync(string timelineName, long ownerId);
+
+ /// <summary>
+ /// Delete a timeline.
+ /// </summary>
+ /// <param name="id">The id of the timeline to delete.</param>
+ /// <exception cref="TimelineNotExistException">Thrown when the timeline does not exist.</exception>
+ Task DeleteTimelineAsync(long id);
+ }
+}
diff --git a/BackEnd/Timeline/Services/Timeline/Resource.Designer.cs b/BackEnd/Timeline/Services/Timeline/Resource.Designer.cs
index 31fd6320..6faea295 100644
--- a/BackEnd/Timeline/Services/Timeline/Resource.Designer.cs
+++ b/BackEnd/Timeline/Services/Timeline/Resource.Designer.cs
@@ -61,6 +61,15 @@ namespace Timeline.Services.Timeline {
}
/// <summary>
+ /// Looks up a localized string similar to This timeline name is neither a valid personal timeline name nor a valid ordinary timeline name. {0}.
+ /// </summary>
+ internal static string ExceptionGeneralTimelineNameBadFormat {
+ get {
+ return ResourceManager.GetString("ExceptionGeneralTimelineNameBadFormat", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Timeline with given constraints already exist..
/// </summary>
internal static string ExceptionTimelineAlreadyExist {
@@ -113,5 +122,14 @@ namespace Timeline.Services.Timeline {
return ResourceManager.GetString("ExceptionTimelinePostNoExistReasonNotCreated", resourceCulture);
}
}
+
+ /// <summary>
+ /// Looks up a localized string similar to A personal timeline for user with username={0} is created automatically..
+ /// </summary>
+ internal static string LogPersonalTimelineAutoCreate {
+ get {
+ return ResourceManager.GetString("LogPersonalTimelineAutoCreate", resourceCulture);
+ }
+ }
}
}
diff --git a/BackEnd/Timeline/Services/Timeline/Resource.resx b/BackEnd/Timeline/Services/Timeline/Resource.resx
index 7fd7b5c7..3a233e55 100644
--- a/BackEnd/Timeline/Services/Timeline/Resource.resx
+++ b/BackEnd/Timeline/Services/Timeline/Resource.resx
@@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
+ <data name="ExceptionGeneralTimelineNameBadFormat" xml:space="preserve">
+ <value>This timeline name is neither a valid personal timeline name nor a valid ordinary timeline name. {0}</value>
+ </data>
<data name="ExceptionTimelineAlreadyExist" xml:space="preserve">
<value>Timeline with given constraints already exist.</value>
</data>
@@ -135,4 +138,7 @@
<data name="ExceptionTimelinePostNoExistReasonNotCreated" xml:space="preserve">
<value>it has not been created</value>
</data>
+ <data name="LogPersonalTimelineAutoCreate" xml:space="preserve">
+ <value>A personal timeline for user with username={0} is created automatically.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/BackEnd/Timeline/Services/Timeline/TimelineChangePropertyParams.cs b/BackEnd/Timeline/Services/Timeline/TimelineChangePropertyParams.cs
new file mode 100644
index 00000000..57c5c90c
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/TimelineChangePropertyParams.cs
@@ -0,0 +1,13 @@
+using Timeline.Models;
+
+namespace Timeline.Services.Timeline
+{
+ public class TimelineChangePropertyParams
+ {
+ public string? Name { get; set; }
+ public string? Title { get; set; }
+ public string? Description { get; set; }
+ public TimelineVisibility? Visibility { get; set; }
+ public string? Color { get; set; }
+ }
+}
diff --git a/BackEnd/Timeline/Services/Timeline/TimelineHelper.cs b/BackEnd/Timeline/Services/Timeline/TimelineHelper.cs
new file mode 100644
index 00000000..6c75f04c
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/TimelineHelper.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Timeline.Services.Timeline
+{
+ public static class TimelineHelper
+ {
+ public static string ExtractTimelineName(string name, out bool isPersonal)
+ {
+ if (name.StartsWith("@", StringComparison.OrdinalIgnoreCase))
+ {
+ isPersonal = true;
+ return name[1..];
+ }
+ else
+ {
+ isPersonal = false;
+ return name;
+ }
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Services/Timeline/TimelinePostCreateRequest.cs b/BackEnd/Timeline/Services/Timeline/TimelinePostCreateRequest.cs
new file mode 100644
index 00000000..1a56e11a
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/TimelinePostCreateRequest.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+
+namespace Timeline.Services.Timeline
+{
+ public class TimelinePostCreateRequest
+ {
+ public string? Color { get; set; }
+
+ /// <summary>If not set, current time is used.</summary>
+ public DateTime? Time { get; set; }
+
+#pragma warning disable CA2227
+ public List<TimelinePostCreateRequestData> DataList { get; set; } = new List<TimelinePostCreateRequestData>();
+#pragma warning restore CA2227
+ }
+}
diff --git a/BackEnd/Timeline/Services/Timeline/TimelinePostCreateRequestData.cs b/BackEnd/Timeline/Services/Timeline/TimelinePostCreateRequestData.cs
new file mode 100644
index 00000000..a8ea3c3b
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/TimelinePostCreateRequestData.cs
@@ -0,0 +1,16 @@
+namespace Timeline.Services.Timeline
+{
+ 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
+ }
+}
diff --git a/BackEnd/Timeline/Services/Timeline/TimelinePostPatchRequest.cs b/BackEnd/Timeline/Services/Timeline/TimelinePostPatchRequest.cs
new file mode 100644
index 00000000..92403a07
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/TimelinePostPatchRequest.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Timeline.Services.Timeline
+{
+ public class TimelinePostPatchRequest
+ {
+ public string? Color { get; set; }
+ public DateTime? Time { get; set; }
+ }
+}
diff --git a/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs b/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs
index d18e65e0..65a01b37 100644
--- a/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs
+++ b/BackEnd/Timeline/Services/Timeline/TimelinePostService.cs
@@ -16,150 +16,6 @@ using Timeline.Services.User;
namespace Timeline.Services.Timeline
{
- 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; }
-
- /// <summary>If not set, current time is used.</summary>
- public DateTime? Time { get; set; }
-
-#pragma warning disable CA2227
- public List<TimelinePostCreateRequestData> DataList { get; set; } = new List<TimelinePostCreateRequestData>();
-#pragma warning restore CA2227
- }
-
- public class TimelinePostPatchRequest
- {
- public string? Color { get; set; }
- public DateTime? Time { get; set; }
- }
-
- public interface ITimelinePostService
- {
- /// <summary>
- /// Get all the posts in the timeline.
- /// </summary>
- /// <param name="timelineId">The id of the timeline.</param>
- /// <param name="modifiedSince">The time that posts have been modified since.</param>
- /// <param name="includeDeleted">Whether include deleted posts.</param>
- /// <returns>A list of all posts.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- Task<List<TimelinePostEntity>> GetPosts(long timelineId, DateTime? modifiedSince = null, bool includeDeleted = false);
-
- /// <summary>
- /// Get a post of a timeline.
- /// </summary>
- /// <param name="timelineId">The id of the timeline of the post.</param>
- /// <param name="postId">The id of the post.</param>
- /// <param name="includeDeleted">If true, return the entity even if it is deleted.</param>
- /// <returns>The post.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
- Task<TimelinePostEntity> GetPost(long timelineId, long postId, bool includeDeleted = false);
-
- /// <summary>
- /// Get the data digest of a post.
- /// </summary>
- /// <param name="timelineId">The timeline id.</param>
- /// <param name="postId">The post id.</param>
- /// <param name="dataIndex">The index of the data.</param>
- /// <returns>The data digest.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
- /// <exception cref="TimelinePostDataNotExistException">Thrown when data of that index does not exist.</exception>
- Task<ICacheableDataDigest> GetPostDataDigest(long timelineId, long postId, long dataIndex);
-
- /// <summary>
- /// Get the data of a post.
- /// </summary>
- /// <param name="timelineId">The timeline id.</param>
- /// <param name="postId">The post id.</param>
- /// <param name="dataIndex">The index of the data.</param>
- /// <returns>The data.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
- /// <exception cref="TimelinePostDataNotExistException">Thrown when data of that index does not exist.</exception>
- Task<ByteData> GetPostData(long timelineId, long postId, long dataIndex);
-
- /// <summary>
- /// Create a new post in timeline.
- /// </summary>
- /// <param name="timelineId">The id of the timeline to create post against.</param>
- /// <param name="authorId">The author's user id.</param>
- /// <param name="request">Info about the post.</param>
- /// <returns>The entity of the created post.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="request"/> is of invalid format.</exception>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="UserNotExistException">Thrown if user of <paramref name="authorId"/> does not exist.</exception>
- /// <exception cref="ImageException">Thrown if data is not a image. Validated by <see cref="ImageService"/>.</exception>
- Task<TimelinePostEntity> CreatePost(long timelineId, long authorId, TimelinePostCreateRequest request);
-
- /// <summary>
- /// Modify a post. Change its properties or replace its content.
- /// </summary>
- /// <param name="timelineId">The timeline id.</param>
- /// <param name="postId">The post id.</param>
- /// <param name="request">The request.</param>
- /// <returns>The entity of the patched post.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="request"/> is of invalid format.</exception>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when post does not exist.</exception>
- Task<TimelinePostEntity> PatchPost(long timelineId, long postId, TimelinePostPatchRequest request);
-
- /// <summary>
- /// Delete a post.
- /// </summary>
- /// <param name="timelineId">The id of the timeline to delete post against.</param>
- /// <param name="postId">The id of the post to delete.</param>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when the post with given id does not exist or is deleted already.</exception>
- /// <remarks>
- /// First use <see cref="HasPostModifyPermission(long, long, long, bool)"/> to check the permission.
- /// </remarks>
- Task DeletePost(long timelineId, long postId);
-
- /// <summary>
- /// Delete all posts of the given user. Used when delete a user.
- /// </summary>
- /// <param name="userId">The id of the user.</param>
- Task DeleteAllPostsOfUser(long userId);
-
- /// <summary>
- /// Verify whether a user has the permission to modify a post.
- /// </summary>
- /// <param name="timelineId">The id of the timeline.</param>
- /// <param name="postId">The id of the post.</param>
- /// <param name="modifierId">The id of the user to check on.</param>
- /// <param name="throwOnPostNotExist">True if you want it to throw <see cref="TimelinePostNotExistException"/>. Default false.</param>
- /// <returns>True if can modify, false if can't modify.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when the post with given id does not exist or is deleted already and <paramref name="throwOnPostNotExist"/> is true.</exception>
- /// <remarks>
- /// Unless <paramref name="throwOnPostNotExist"/> 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 <paramref name="throwOnPostNotExist"/> 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.
- /// </remarks>
- Task<bool> HasPostModifyPermission(long timelineId, long postId, long modifierId, bool throwOnPostNotExist = false);
- }
-
public class TimelinePostService : ITimelinePostService
{
private readonly ILogger<TimelinePostService> _logger;
@@ -184,7 +40,7 @@ namespace Timeline.Services.Timeline
private async Task CheckTimelineExistence(long timelineId)
{
- if (!await _basicTimelineService.CheckExistence(timelineId))
+ if (!await _basicTimelineService.CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
}
@@ -194,7 +50,7 @@ namespace Timeline.Services.Timeline
throw new UserNotExistException(userId);
}
- public async Task<List<TimelinePostEntity>> GetPosts(long timelineId, DateTime? modifiedSince = null, bool includeDeleted = false)
+ public async Task<List<TimelinePostEntity>> GetPostsAsync(long timelineId, DateTime? modifiedSince = null, bool includeDeleted = false)
{
await CheckTimelineExistence(timelineId);
@@ -217,7 +73,7 @@ namespace Timeline.Services.Timeline
return await query.ToListAsync();
}
- public async Task<TimelinePostEntity> GetPost(long timelineId, long postId, bool includeDeleted = false)
+ public async Task<TimelinePostEntity> GetPostAsync(long timelineId, long postId, bool includeDeleted = false)
{
await CheckTimelineExistence(timelineId);
@@ -236,7 +92,7 @@ namespace Timeline.Services.Timeline
return post;
}
- public async Task<ICacheableDataDigest> GetPostDataDigest(long timelineId, long postId, long dataIndex)
+ public async Task<ICacheableDataDigest> GetPostDataDigestAsync(long timelineId, long postId, long dataIndex)
{
await CheckTimelineExistence(timelineId);
@@ -256,7 +112,7 @@ namespace Timeline.Services.Timeline
return new CacheableDataDigest(dataEntity.DataTag, dataEntity.LastUpdated);
}
- public async Task<ByteData> GetPostData(long timelineId, long postId, long dataIndex)
+ public async Task<ByteData> GetPostDataAsync(long timelineId, long postId, long dataIndex)
{
await CheckTimelineExistence(timelineId);
@@ -278,7 +134,7 @@ namespace Timeline.Services.Timeline
return new ByteData(data, dataEntity.Kind);
}
- public async Task<TimelinePostEntity> CreatePost(long timelineId, long authorId, TimelinePostCreateRequest request)
+ public async Task<TimelinePostEntity> CreatePostAsync(long timelineId, long authorId, TimelinePostCreateRequest request)
{
if (request is null)
throw new ArgumentNullException(nameof(request));
@@ -382,7 +238,7 @@ namespace Timeline.Services.Timeline
return postEntity;
}
- public async Task<TimelinePostEntity> PatchPost(long timelineId, long postId, TimelinePostPatchRequest request)
+ public async Task<TimelinePostEntity> PatchPostAsync(long timelineId, long postId, TimelinePostPatchRequest request)
{
if (request is null)
throw new ArgumentNullException(nameof(request));
@@ -417,7 +273,7 @@ namespace Timeline.Services.Timeline
return entity;
}
- public async Task DeletePost(long timelineId, long postId)
+ public async Task DeletePostAsync(long timelineId, long postId)
{
await CheckTimelineExistence(timelineId);
@@ -448,17 +304,17 @@ namespace Timeline.Services.Timeline
await transaction.CommitAsync();
}
- public async Task DeleteAllPostsOfUser(long userId)
+ public async Task DeleteAllPostsOfUserAsync(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);
+ await this.DeletePostAsync(postEntity.TimelineId, postEntity.LocalId);
}
}
- public async Task<bool> HasPostModifyPermission(long timelineId, long postId, long modifierId, bool throwOnPostNotExist = false)
+ public async Task<bool> HasPostModifyPermissionAsync(long timelineId, long postId, long modifierId, bool throwOnPostNotExist = false)
{
await CheckTimelineExistence(timelineId);
diff --git a/BackEnd/Timeline/Services/Timeline/TimelineService.cs b/BackEnd/Timeline/Services/Timeline/TimelineService.cs
index 0086726e..cea93272 100644
--- a/BackEnd/Timeline/Services/Timeline/TimelineService.cs
+++ b/BackEnd/Timeline/Services/Timeline/TimelineService.cs
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -10,173 +11,10 @@ using Timeline.Services.User;
namespace Timeline.Services.Timeline
{
- public static class TimelineHelper
- {
- public static string ExtractTimelineName(string name, out bool isPersonal)
- {
- if (name.StartsWith("@", StringComparison.OrdinalIgnoreCase))
- {
- isPersonal = true;
- return name[1..];
- }
- else
- {
- isPersonal = false;
- return name;
- }
- }
- }
-
- public enum TimelineUserRelationshipType
- {
- Own = 0b1,
- Join = 0b10,
- Default = Own | Join
- }
-
- public class TimelineUserRelationship
- {
- public TimelineUserRelationship(TimelineUserRelationshipType type, long userId)
- {
- Type = type;
- UserId = userId;
- }
-
- public TimelineUserRelationshipType Type { get; set; }
- public long UserId { get; set; }
- }
-
- public class TimelineChangePropertyParams
- {
- public string? Name { get; set; }
- public string? Title { get; set; }
- public string? Description { get; set; }
- public TimelineVisibility? Visibility { get; set; }
- public string? Color { get; set; }
- }
-
- /// <summary>
- /// This define the interface of both personal timeline and ordinary timeline.
- /// </summary>
- public interface ITimelineService : IBasicTimelineService
- {
- /// <summary>
- /// Get the timeline info.
- /// </summary>
- /// <param name="id">Id of timeline.</param>
- /// <returns>The timeline info.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- Task<TimelineEntity> GetTimeline(long id);
-
- /// <summary>
- /// Set the properties of a timeline.
- /// </summary>
- /// <param name="id">The id of the timeline.</param>
- /// <param name="newProperties">The new properties. Null member means not to change.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="newProperties"/> is null.</exception>
- /// <exception cref="TimelineNotExistException">Thrown when timeline with given id does not exist.</exception>
- /// <exception cref="EntityAlreadyExistException">Thrown when a timeline with new name already exists.</exception>
- Task ChangeProperty(long id, TimelineChangePropertyParams newProperties);
-
- /// <summary>
- /// Add a member to timeline.
- /// </summary>
- /// <param name="timelineId">Timeline id.</param>
- /// <param name="userId">User id.</param>
- /// <returns>True if the memeber was added. False if it is already a member.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user does not exist.</exception>
- Task<bool> AddMember(long timelineId, long userId);
-
- /// <summary>
- /// Remove a member from timeline.
- /// </summary>
- /// <param name="timelineId">Timeline id.</param>
- /// <param name="userId">User id.</param>
- /// <returns>True if the memeber was removed. False if it was not a member before.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user does not exist.</exception>
- Task<bool> RemoveMember(long timelineId, long userId);
-
- /// <summary>
- /// Check whether a user can manage(change timeline info, member, ...) a timeline.
- /// </summary>
- /// <param name="timelineId">The id of the timeline.</param>
- /// <param name="userId">The id of the user to check on.</param>
- /// <returns>True if the user can manage the timeline, otherwise false.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <remarks>
- /// This method does not check whether visitor is administrator.
- /// Return false if user with user id does not exist.
- /// </remarks>
- Task<bool> HasManagePermission(long timelineId, long userId);
-
- /// <summary>
- /// Verify whether a visitor has the permission to read a timeline.
- /// </summary>
- /// <param name="timelineId">The id of the timeline.</param>
- /// <param name="visitorId">The id of the user to check on. Null means visitor without account.</param>
- /// <returns>True if can read, false if can't read.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <remarks>
- /// This method does not check whether visitor is administrator.
- /// Return false if user with visitor id does not exist.
- /// </remarks>
- Task<bool> HasReadPermission(long timelineId, long? visitorId);
-
- /// <summary>
- /// Verify whether a user is member of a timeline.
- /// </summary>
- /// <param name="timelineId">The id of the timeline.</param>
- /// <param name="userId">The id of user to check on.</param>
- /// <returns>True if it is a member, false if not.</returns>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <remarks>
- /// Timeline owner is also considered as a member.
- /// Return false when user with user id does not exist.
- /// </remarks>
- Task<bool> IsMemberOf(long timelineId, long userId);
-
- /// <summary>
- /// Get all timelines including personal and ordinary timelines.
- /// </summary>
- /// <param name="relate">Filter timelines related (own or is a member) to specific user.</param>
- /// <param name="visibility">Filter timelines with given visibility. If null or empty, all visibilities are returned. Duplicate value are ignored.</param>
- /// <returns>The list of timelines.</returns>
- /// <remarks>
- /// If user with related user id does not exist, empty list will be returned.
- /// </remarks>
- Task<List<TimelineEntity>> GetTimelines(TimelineUserRelationship? relate = null, List<TimelineVisibility>? visibility = null);
-
- /// <summary>
- /// Create a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="ownerId">The id of owner of the timeline.</param>
- /// <returns>The info of the new timeline.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when timeline name is invalid.</exception>
- /// <exception cref="EntityAlreadyExistException">Thrown when the timeline already exists.</exception>
- /// <exception cref="UserNotExistException">Thrown when the owner user does not exist.</exception>
- Task<TimelineEntity> CreateTimeline(string timelineName, long ownerId);
-
- /// <summary>
- /// Delete a timeline.
- /// </summary>
- /// <param name="id">The id of the timeline to delete.</param>
- /// <exception cref="TimelineNotExistException">Thrown when the timeline does not exist.</exception>
- Task DeleteTimeline(long id);
- }
public class TimelineService : BasicTimelineService, ITimelineService
{
- public TimelineService(DatabaseContext database, IBasicUserService userService, IClock clock)
- : base(database, userService, clock)
- {
- _database = database;
- _userService = userService;
- _clock = clock;
- }
+ private readonly ILogger<TimelineService> _logger;
private readonly DatabaseContext _database;
@@ -188,6 +26,16 @@ namespace Timeline.Services.Timeline
private readonly ColorValidator _colorValidator = new ColorValidator();
+ public TimelineService(ILoggerFactory loggerFactory, DatabaseContext database, IBasicUserService userService, IClock clock)
+ : base(loggerFactory, database, userService, clock)
+ {
+ _logger = loggerFactory.CreateLogger<TimelineService>();
+ _database = database;
+ _userService = userService;
+ _clock = clock;
+ }
+
+
private void ValidateTimelineName(string name, string paramName)
{
if (!_timelineNameValidator.Validate(name, out var message))
@@ -196,7 +44,7 @@ namespace Timeline.Services.Timeline
}
}
- public async Task<TimelineEntity> GetTimeline(long id)
+ public async Task<TimelineEntity> GetTimelineAsync(long id)
{
var entity = await _database.Timelines.Where(t => t.Id == id).SingleOrDefaultAsync();
@@ -206,7 +54,7 @@ namespace Timeline.Services.Timeline
return entity;
}
- public async Task ChangeProperty(long id, TimelineChangePropertyParams newProperties)
+ public async Task ChangePropertyAsync(long id, TimelineChangePropertyParams newProperties)
{
if (newProperties is null)
throw new ArgumentNullException(nameof(newProperties));
@@ -279,9 +127,9 @@ namespace Timeline.Services.Timeline
await _database.SaveChangesAsync();
}
- public async Task<bool> AddMember(long timelineId, long userId)
+ public async Task<bool> AddMemberAsync(long timelineId, long userId)
{
- if (!await CheckExistence(timelineId))
+ if (!await CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
if (!await _userService.CheckUserExistenceAsync(userId))
@@ -301,9 +149,9 @@ namespace Timeline.Services.Timeline
return true;
}
- public async Task<bool> RemoveMember(long timelineId, long userId)
+ public async Task<bool> RemoveMemberAsync(long timelineId, long userId)
{
- if (!await CheckExistence(timelineId))
+ if (!await CheckTimelineExistenceAsync(timelineId))
throw new TimelineNotExistException(timelineId);
if (!await _userService.CheckUserExistenceAsync(userId))
@@ -321,7 +169,7 @@ namespace Timeline.Services.Timeline
return true;
}
- public async Task<bool> HasManagePermission(long timelineId, long userId)
+ public async Task<bool> HasManagePermissionAsync(long timelineId, long userId)
{
var entity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleOrDefaultAsync();
@@ -331,7 +179,7 @@ namespace Timeline.Services.Timeline
return entity.OwnerId == userId;
}
- public async Task<bool> HasReadPermission(long timelineId, long? visitorId)
+ public async Task<bool> HasReadPermissionAsync(long timelineId, long? visitorId)
{
var entity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.Visibility }).SingleOrDefaultAsync();
@@ -355,7 +203,7 @@ namespace Timeline.Services.Timeline
}
}
- public async Task<bool> IsMemberOf(long timelineId, long userId)
+ public async Task<bool> IsMemberOfAsync(long timelineId, long userId)
{
var entity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleOrDefaultAsync();
@@ -368,7 +216,7 @@ namespace Timeline.Services.Timeline
return await _database.TimelineMembers.AnyAsync(m => m.TimelineId == timelineId && m.UserId == userId);
}
- public async Task<List<TimelineEntity>> GetTimelines(TimelineUserRelationship? relate = null, List<TimelineVisibility>? visibility = null)
+ public async Task<List<TimelineEntity>> GetTimelinesAsync(TimelineUserRelationship? relate = null, List<TimelineVisibility>? visibility = null)
{
List<TimelineEntity> entities;
@@ -405,7 +253,7 @@ namespace Timeline.Services.Timeline
return entities;
}
- public async Task<TimelineEntity> CreateTimeline(string name, long owner)
+ public async Task<TimelineEntity> CreateTimelineAsync(string name, long owner)
{
if (name == null)
throw new ArgumentNullException(nameof(name));
@@ -425,7 +273,7 @@ namespace Timeline.Services.Timeline
return entity;
}
- public async Task DeleteTimeline(long id)
+ public async Task DeleteTimelineAsync(long id)
{
var entity = await _database.Timelines.Where(t => t.Id == id).SingleOrDefaultAsync();
@@ -436,17 +284,4 @@ namespace Timeline.Services.Timeline
await _database.SaveChangesAsync();
}
}
-
- public static class TimelineServiceExtensions
- {
- public static async Task<List<TimelineEntity>> GetTimelineList(this ITimelineService service, IEnumerable<long> ids)
- {
- var timelines = new List<TimelineEntity>();
- foreach (var id in ids)
- {
- timelines.Add(await service.GetTimeline(id));
- }
- return timelines;
- }
- }
}
diff --git a/BackEnd/Timeline/Services/Timeline/TimelineServiceExtensions.cs b/BackEnd/Timeline/Services/Timeline/TimelineServiceExtensions.cs
new file mode 100644
index 00000000..7b745168
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/TimelineServiceExtensions.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Timeline.Entities;
+
+namespace Timeline.Services.Timeline
+{
+ public static class TimelineServiceExtensions
+ {
+ public static async Task<List<TimelineEntity>> GetTimelineList(this ITimelineService service, IEnumerable<long> ids)
+ {
+ var timelines = new List<TimelineEntity>();
+ foreach (var id in ids)
+ {
+ timelines.Add(await service.GetTimelineAsync(id));
+ }
+ return timelines;
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Services/Timeline/TimelineUserRelationship.cs b/BackEnd/Timeline/Services/Timeline/TimelineUserRelationship.cs
new file mode 100644
index 00000000..70789e6f
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/TimelineUserRelationship.cs
@@ -0,0 +1,14 @@
+namespace Timeline.Services.Timeline
+{
+ public class TimelineUserRelationship
+ {
+ public TimelineUserRelationship(TimelineUserRelationshipType type, long userId)
+ {
+ Type = type;
+ UserId = userId;
+ }
+
+ public TimelineUserRelationshipType Type { get; set; }
+ public long UserId { get; set; }
+ }
+}
diff --git a/BackEnd/Timeline/Services/Timeline/TimelineUserRelationshipType.cs b/BackEnd/Timeline/Services/Timeline/TimelineUserRelationshipType.cs
new file mode 100644
index 00000000..4c063cc0
--- /dev/null
+++ b/BackEnd/Timeline/Services/Timeline/TimelineUserRelationshipType.cs
@@ -0,0 +1,9 @@
+namespace Timeline.Services.Timeline
+{
+ public enum TimelineUserRelationshipType
+ {
+ Own = 0b1,
+ Join = 0b10,
+ Default = Own | Join
+ }
+}
diff --git a/BackEnd/Timeline/Services/Token/IUserTokenManager.cs b/BackEnd/Timeline/Services/Token/IUserTokenManager.cs
index c6eaa5b7..bdc1add3 100644
--- a/BackEnd/Timeline/Services/Token/IUserTokenManager.cs
+++ b/BackEnd/Timeline/Services/Token/IUserTokenManager.cs
@@ -18,7 +18,7 @@ namespace Timeline.Services.Token
/// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
/// <exception cref="UserNotExistException">Thrown when the user with <paramref name="username"/> does not exist.</exception>
/// <exception cref="BadPasswordException">Thrown when <paramref name="password"/> is wrong.</exception>
- public Task<UserTokenCreateResult> CreateToken(string username, string password, DateTime? expireAt = null);
+ public Task<UserTokenCreateResult> CreateTokenAsync(string username, string password, DateTime? expireAt = null);
/// <summary>
/// Verify a token and get the saved user info. This also check the database for existence of the user.
@@ -30,6 +30,6 @@ namespace Timeline.Services.Token
/// <exception cref="UserTokenVersionExpiredException">Thrown when the token is of bad version.</exception>
/// <exception cref="UserTokenBadFormatException">Thrown when the token is of bad format.</exception>
/// <exception cref="UserTokenUserNotExistException">Thrown when the user specified by the token does not exist. Usually the user had been deleted after the token was issued.</exception>
- public Task<UserEntity> VerifyToken(string token);
+ public Task<UserEntity> VerifyTokenAsync(string token);
}
}
diff --git a/BackEnd/Timeline/Services/Token/UserTokenManager.cs b/BackEnd/Timeline/Services/Token/UserTokenManager.cs
index 1d5348a5..5aa85a5e 100644
--- a/BackEnd/Timeline/Services/Token/UserTokenManager.cs
+++ b/BackEnd/Timeline/Services/Token/UserTokenManager.cs
@@ -26,7 +26,7 @@ namespace Timeline.Services.Token
_clock = clock;
}
- public async Task<UserTokenCreateResult> CreateToken(string username, string password, DateTime? expireAt = null)
+ public async Task<UserTokenCreateResult> CreateTokenAsync(string username, string password, DateTime? expireAt = null)
{
expireAt = expireAt?.MyToUtc();
@@ -51,7 +51,7 @@ namespace Timeline.Services.Token
}
- public async Task<UserEntity> VerifyToken(string token)
+ public async Task<UserEntity> VerifyTokenAsync(string token)
{
if (token == null)
throw new ArgumentNullException(nameof(token));
diff --git a/BackEnd/Timeline/Services/User/UserDeleteService.cs b/BackEnd/Timeline/Services/User/UserDeleteService.cs
index e0391841..3e3e29e2 100644
--- a/BackEnd/Timeline/Services/User/UserDeleteService.cs
+++ b/BackEnd/Timeline/Services/User/UserDeleteService.cs
@@ -44,7 +44,7 @@ namespace Timeline.Services.User
if (user.Id == 1)
throw new InvalidOperationOnRootUserException(Resource.ExceptionDeleteRootUser);
- await _timelinePostService.DeleteAllPostsOfUser(user.Id);
+ await _timelinePostService.DeleteAllPostsOfUserAsync(user.Id);
_databaseContext.Users.Remove(user);