aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-11-26 20:02:03 +0800
committercrupest <crupest@outlook.com>2020-11-26 20:02:03 +0800
commit080330966333fe61b6a9d5413c6b05b9ea77f4dc (patch)
tree69364aa51ae18083e75b486e3ab5bb6542061860
parentc69a18f66721404dac3a04a090d04bf248964a9f (diff)
downloadtimeline-080330966333fe61b6a9d5413c6b05b9ea77f4dc.tar.gz
timeline-080330966333fe61b6a9d5413c6b05b9ea77f4dc.tar.bz2
timeline-080330966333fe61b6a9d5413c6b05b9ea77f4dc.zip
feat: Add highlight timeline entity and service.
-rw-r--r--BackEnd/Timeline.Tests/Services/HighlightTimelineServiceTest.cs22
-rw-r--r--BackEnd/Timeline/Entities/DatabaseContext.cs2
-rw-r--r--BackEnd/Timeline/Entities/HighlightTimelineEntity.cs24
-rw-r--r--BackEnd/Timeline/Services/Exceptions/TimelineNotExistException.cs7
-rw-r--r--BackEnd/Timeline/Services/HighlightTimelineService.cs112
-rw-r--r--BackEnd/Timeline/Services/TimelineService.cs43
-rw-r--r--BackEnd/Timeline/Services/UserService.cs12
7 files changed, 221 insertions, 1 deletions
diff --git a/BackEnd/Timeline.Tests/Services/HighlightTimelineServiceTest.cs b/BackEnd/Timeline.Tests/Services/HighlightTimelineServiceTest.cs
new file mode 100644
index 00000000..950fa974
--- /dev/null
+++ b/BackEnd/Timeline.Tests/Services/HighlightTimelineServiceTest.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Timeline.Services;
+
+namespace Timeline.Tests.Services
+{
+ public class HighlightTimelineServiceTest : DatabaseBasedTest
+ {
+ private UserService _userService;
+ private TimelineService _timelineService;
+
+ private HighlightTimelineService _service;
+
+ protected override void OnDatabaseCreated()
+ {
+
+ }
+
+ }
+}
diff --git a/BackEnd/Timeline/Entities/DatabaseContext.cs b/BackEnd/Timeline/Entities/DatabaseContext.cs
index e4203392..4205c2cf 100644
--- a/BackEnd/Timeline/Entities/DatabaseContext.cs
+++ b/BackEnd/Timeline/Entities/DatabaseContext.cs
@@ -29,6 +29,8 @@ namespace Timeline.Entities
public DbSet<TimelineEntity> Timelines { get; set; } = default!;
public DbSet<TimelinePostEntity> TimelinePosts { get; set; } = default!;
public DbSet<TimelineMemberEntity> TimelineMembers { get; set; } = default!;
+ public DbSet<HighlightTimelineEntity> HighlightTimelines { get; set; } = default!;
+
public DbSet<JwtTokenEntity> JwtToken { get; set; } = default!;
public DbSet<DataEntity> Data { get; set; } = default!;
}
diff --git a/BackEnd/Timeline/Entities/HighlightTimelineEntity.cs b/BackEnd/Timeline/Entities/HighlightTimelineEntity.cs
new file mode 100644
index 00000000..0a38c8a6
--- /dev/null
+++ b/BackEnd/Timeline/Entities/HighlightTimelineEntity.cs
@@ -0,0 +1,24 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Timeline.Entities
+{
+ [Table("highlight_timelines")]
+ public record HighlightTimelineEntity
+ {
+ [Key, Column("id"), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public long Id { get; set; }
+
+ [Column("timeline_id")]
+ public long TimelineId { get; set; }
+
+ [ForeignKey(nameof(TimelineId))]
+ public TimelineEntity Timeline { get; set; } = default!;
+
+ [Column("operator_id")]
+ public long? OperatorId { get; set; }
+
+ [ForeignKey(nameof(OperatorId))]
+ public UserEntity? Operator { get; set; }
+ }
+}
diff --git a/BackEnd/Timeline/Services/Exceptions/TimelineNotExistException.cs b/BackEnd/Timeline/Services/Exceptions/TimelineNotExistException.cs
index 70970b24..ef882ffe 100644
--- a/BackEnd/Timeline/Services/Exceptions/TimelineNotExistException.cs
+++ b/BackEnd/Timeline/Services/Exceptions/TimelineNotExistException.cs
@@ -6,7 +6,11 @@ namespace Timeline.Services.Exceptions
[Serializable]
public class TimelineNotExistException : EntityNotExistException
{
- public TimelineNotExistException() : this(null, null) { }
+ public TimelineNotExistException() : this((long?)null) { }
+ public TimelineNotExistException(long? id) : this(id, null) { }
+ public TimelineNotExistException(long? id, Exception? inner) : this(id, null, inner) { }
+ public TimelineNotExistException(long? id, string? message, Exception? inner) : base(EntityNames.Timeline, null, message, inner) { TimelineId = id; }
+
public TimelineNotExistException(string? timelineName) : this(timelineName, null) { }
public TimelineNotExistException(string? timelineName, Exception? inner) : this(timelineName, null, inner) { }
public TimelineNotExistException(string? timelineName, string? message, Exception? inner = null)
@@ -17,5 +21,6 @@ namespace Timeline.Services.Exceptions
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
public string? TimelineName { get; set; }
+ public long? TimelineId { get; set; }
}
}
diff --git a/BackEnd/Timeline/Services/HighlightTimelineService.cs b/BackEnd/Timeline/Services/HighlightTimelineService.cs
new file mode 100644
index 00000000..7528d9b0
--- /dev/null
+++ b/BackEnd/Timeline/Services/HighlightTimelineService.cs
@@ -0,0 +1,112 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Timeline.Entities;
+using Timeline.Services.Exceptions;
+
+namespace Timeline.Services
+{
+ public interface IHighlightTimelineService
+ {
+ /// <summary>
+ /// Get all highlight timelines.
+ /// </summary>
+ /// <returns>A list of all highlight timelines.</returns>
+ Task<List<Models.Timeline>> GetHighlightTimelines();
+
+ /// <summary>
+ /// Add a timeline to highlight list.
+ /// </summary>
+ /// <param name="timelineName">The timeline name.</param>
+ /// <param name="operatorId">The user id of operator.</param>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="timelineName"/> is not a valid timeline name.</exception>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline with given name does not exist.</exception>
+ /// <exception cref="UserNotExistException">Thrown when user with given operator id does not exist.</exception>
+ Task AddHighlightTimeline(string timelineName, long? operatorId);
+
+ /// <summary>
+ /// Remove a timeline from highlight list.
+ /// </summary>
+ /// <param name="timelineName">The timeline name.</param>
+ /// <param name="operatorId">The user id of operator.</param>
+ /// <returns>True if deletion is actually performed. Otherwise false (timeline was not in the list).</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown when <paramref name="timelineName"/> is not a valid timeline name.</exception>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline with given name does not exist.</exception>
+ /// <exception cref="UserNotExistException">Thrown when user with given operator id does not exist.</exception>
+ Task<bool> RemoveHighlightTimeline(string timelineName, long? operatorId);
+ }
+
+ public class HighlightTimelineService : IHighlightTimelineService
+ {
+ private readonly DatabaseContext _database;
+ private readonly IUserService _userService;
+ private readonly ITimelineService _timelineService;
+
+ public HighlightTimelineService(DatabaseContext database, IUserService userService, ITimelineService timelineService)
+ {
+ _database = database;
+ _userService = userService;
+ _timelineService = timelineService;
+ }
+
+ public async Task AddHighlightTimeline(string timelineName, long? operatorId)
+ {
+ if (timelineName == null)
+ throw new ArgumentNullException(nameof(timelineName));
+
+ var timelineId = await _timelineService.GetTimelineIdByName(timelineName);
+
+ if (operatorId.HasValue && !await _userService.CheckUserExistence(operatorId.Value))
+ {
+ throw new UserNotExistException(null, operatorId.Value, "User with given operator id does not exist.", null);
+ }
+
+ var alreadyIs = await _database.HighlightTimelines.AnyAsync(t => t.TimelineId == timelineId);
+
+ if (alreadyIs) return;
+
+ _database.HighlightTimelines.Add(new HighlightTimelineEntity { TimelineId = timelineId, OperatorId = operatorId });
+ await _database.SaveChangesAsync();
+ }
+
+ public async Task<List<Models.Timeline>> GetHighlightTimelines()
+ {
+ var entities = await _database.HighlightTimelines.Select(t => new { t.Id }).ToListAsync();
+
+ var result = new List<Models.Timeline>();
+
+ foreach (var entity in entities)
+ {
+ result.Add(await _timelineService.GetTimelineById(entity.Id));
+ }
+
+ return result;
+ }
+
+ public async Task<bool> RemoveHighlightTimeline(string timelineName, long? operatorId)
+ {
+ if (timelineName == null)
+ throw new ArgumentNullException(nameof(timelineName));
+
+ var timelineId = await _timelineService.GetTimelineIdByName(timelineName);
+
+ if (operatorId.HasValue && !await _userService.CheckUserExistence(operatorId.Value))
+ {
+ throw new UserNotExistException(null, operatorId.Value, "User with given operator id does not exist.", null);
+ }
+
+ var entity = await _database.HighlightTimelines.SingleOrDefaultAsync(t => t.TimelineId == timelineId);
+
+ if (entity == null) return false;
+
+ _database.HighlightTimelines.Remove(entity);
+ await _database.SaveChangesAsync();
+
+ return true;
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Services/TimelineService.cs b/BackEnd/Timeline/Services/TimelineService.cs
index 769e8bed..f8c729bf 100644
--- a/BackEnd/Timeline/Services/TimelineService.cs
+++ b/BackEnd/Timeline/Services/TimelineService.cs
@@ -80,6 +80,19 @@ namespace Timeline.Services
Task<DateTime> GetTimelineLastModifiedTime(string timelineName);
/// <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>
+ Task<long> GetTimelineIdByName(string timelineName);
+
+ /// <summary>
/// Get the timeline unique id.
/// </summary>
/// <param name="timelineName">The name of the timeline.</param>
@@ -106,6 +119,14 @@ namespace Timeline.Services
Task<Models.Timeline> GetTimeline(string timelineName);
/// <summary>
+ /// Get timeline by id.
+ /// </summary>
+ /// <param name="id">Id of timeline.</param>
+ /// <returns>The timeline.</returns>
+ /// <exception cref="TimelineNotExistException">Thrown when timeline with given id does not exist.</exception>
+ Task<Models.Timeline> GetTimelineById(long id);
+
+ /// <summary>
/// Set the properties of a timeline.
/// </summary>
/// <param name="timelineName">The name of the timeline.</param>
@@ -572,6 +593,18 @@ namespace Timeline.Services
return timelineEntity.UniqueId;
}
+ public async Task<long> GetTimelineIdByName(string timelineName)
+ {
+ if (timelineName == null)
+ throw new ArgumentNullException(nameof(timelineName));
+
+ var timelineId = await FindTimelineId(timelineName);
+
+ var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.Id }).SingleAsync();
+
+ return timelineEntity.Id;
+ }
+
public async Task<Models.Timeline> GetTimeline(string timelineName)
{
if (timelineName == null)
@@ -584,6 +617,16 @@ namespace Timeline.Services
return await MapTimelineFromEntity(timelineEntity);
}
+ public async Task<Models.Timeline> GetTimelineById(long id)
+ {
+ var timelineEntity = await _database.Timelines.Where(t => t.Id == id).Include(t => t.Members).SingleOrDefaultAsync();
+
+ if (timelineEntity is null)
+ throw new TimelineNotExistException(id);
+
+ return await MapTimelineFromEntity(timelineEntity);
+ }
+
public async Task<List<TimelinePost>> GetPosts(string timelineName, DateTime? modifiedSince = null, bool includeDeleted = false)
{
modifiedSince = modifiedSince?.MyToUtc();
diff --git a/BackEnd/Timeline/Services/UserService.cs b/BackEnd/Timeline/Services/UserService.cs
index 2c5644cd..76c24666 100644
--- a/BackEnd/Timeline/Services/UserService.cs
+++ b/BackEnd/Timeline/Services/UserService.cs
@@ -39,6 +39,13 @@ namespace Timeline.Services
Task<User> VerifyCredential(string username, string password);
/// <summary>
+ /// Check if a user exists.
+ /// </summary>
+ /// <param name="id">The id of the user.</param>
+ /// <returns>True if exists. Otherwise false.</returns>
+ Task<bool> CheckUserExistence(long id);
+
+ /// <summary>
/// Try to get a user by id.
/// </summary>
/// <param name="id">The id of the user.</param>
@@ -188,6 +195,11 @@ namespace Timeline.Services
return await CreateUserFromEntity(entity);
}
+ public async Task<bool> CheckUserExistence(long id)
+ {
+ return await _databaseContext.Users.AnyAsync(u => u.Id == id);
+ }
+
public async Task<User> GetUser(long id)
{
var user = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();