From 5bf2cebd80a33e3fd6d5ed59e10a76f4d2d5be91 Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 6 Feb 2021 17:43:03 +0800 Subject: ... --- .../Timeline/Controllers/TimelinePostController.cs | 77 ++++++----- .../Timeline/Models/TimelinePostContentTypes.cs | 3 + BackEnd/Timeline/Services/TimelinePostService.cs | 141 ++++++++++++--------- 3 files changed, 118 insertions(+), 103 deletions(-) (limited to 'BackEnd/Timeline') diff --git a/BackEnd/Timeline/Controllers/TimelinePostController.cs b/BackEnd/Timeline/Controllers/TimelinePostController.cs index 0148f56e..4dab2a44 100644 --- a/BackEnd/Timeline/Controllers/TimelinePostController.cs +++ b/BackEnd/Timeline/Controllers/TimelinePostController.cs @@ -162,54 +162,51 @@ namespace Timeline.Controllers return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid()); } - var content = body.Content; + var requestContent = body.Content; - TimelinePostEntity post; + TimelinePostCreateRequestContent createContent; - TimelinePostCommonProperties properties = new TimelinePostCommonProperties { Color = body.Color, Time = body.Time }; - - if (content.Type == TimelinePostContentTypes.Text) + switch (requestContent.Type) { - var text = content.Text; - if (text == null) - { - return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_TextContentTextRequired)); - } - post = await _postService.CreateTextPost(timelineId, userId, text, properties); + case TimelinePostContentTypes.Text: + if (requestContent.Text is null) + { + return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_TextContentTextRequired)); + } + createContent = new TimelinePostCreateRequestTextContent(requestContent.Text); + break; + case TimelinePostContentTypes.Image: + if (requestContent.Data is null) + return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataRequired)); + + // decode base64 + byte[] data; + try + { + data = Convert.FromBase64String(requestContent.Data); + } + catch (FormatException) + { + return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataNotBase64)); + } + + createContent = new TimelinePostCreateRequestImageContent(data); + break; + default: + return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ContentUnknownType)); + } - else if (content.Type == TimelinePostContentTypes.Image) - { - var base64Data = content.Data; - if (base64Data == null) - { - return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataRequired)); - } - byte[] data; - try - { - data = Convert.FromBase64String(base64Data); - } - catch (FormatException) - { - return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataNotBase64)); - } - try - { - post = await _postService.CreateImagePost(timelineId, userId, data, properties); - } - catch (ImageException) - { - return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataNotImage)); - } + try + { + var post = await _postService.CreatePost(timelineId, userId, new TimelinePostCreateRequest(createContent) { Time = body.Time, Color = body.Color }); + var result = await _timelineMapper.MapToHttp(post, timeline, Url); + return result; } - else + catch (ImageException) { - return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ContentUnknownType)); + return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataNotImage)); } - - var result = await _timelineMapper.MapToHttp(post, timeline, Url); - return result; } /// diff --git a/BackEnd/Timeline/Models/TimelinePostContentTypes.cs b/BackEnd/Timeline/Models/TimelinePostContentTypes.cs index 22763eba..ca5e79e1 100644 --- a/BackEnd/Timeline/Models/TimelinePostContentTypes.cs +++ b/BackEnd/Timeline/Models/TimelinePostContentTypes.cs @@ -4,7 +4,10 @@ namespace Timeline.Models { public static class TimelinePostContentTypes { +#pragma warning disable CA1819 // Properties should not return arrays public static string[] AllTypes { get; } = new string[] { Text, Image }; +#pragma warning restore CA1819 // Properties should not return arrays + public const string Text = "text"; public const string Image = "image"; } diff --git a/BackEnd/Timeline/Services/TimelinePostService.cs b/BackEnd/Timeline/Services/TimelinePostService.cs index cf5f4e55..076c45e8 100644 --- a/BackEnd/Timeline/Services/TimelinePostService.cs +++ b/BackEnd/Timeline/Services/TimelinePostService.cs @@ -24,12 +24,50 @@ namespace Timeline.Services public DateTime? LastModified { get; set; } // TODO: Why nullable? } - public class TimelinePostCommonProperties + public abstract class TimelinePostCreateRequestContent { + public abstract string TypeName { get; } + } + + public class TimelinePostCreateRequestTextContent : TimelinePostCreateRequestContent + { + public TimelinePostCreateRequestTextContent(string text) + { + Text = text; + } + + public override string TypeName => TimelinePostContentTypes.Text; + + public string Text { get; set; } + } + + public class TimelinePostCreateRequestImageContent : TimelinePostCreateRequestContent + { + public TimelinePostCreateRequestImageContent(byte[] data) + { + Data = data; + } + + public override string TypeName => TimelinePostContentTypes.Image; + +#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 TimelinePostCreateRequest(TimelinePostCreateRequestContent content) + { + Content = content; + } + public string? Color { get; set; } /// If not set, current time is used. public DateTime? Time { get; set; } + + public TimelinePostCreateRequestContent Content { get; set; } } public interface ITimelinePostService @@ -79,31 +117,18 @@ namespace Timeline.Services Task GetPostData(long timelineId, long postId); /// - /// Create a new text post in timeline. + /// Create a new post in timeline. /// /// The id of the timeline to create post against. /// The author's user id. - /// The content text. - /// Some properties. + /// Info about the post. /// The info of the created post. - /// Thrown when is null. - /// Thrown when timeline does not exist. - /// Thrown if user of does not exist. - Task CreateTextPost(long timelineId, long authorId, string text, TimelinePostCommonProperties? properties = null); - - /// - /// Create a new image post in timeline. - /// - /// The id of the timeline to create post against. - /// The author's user id. - /// The image data. - /// Some properties. - /// The info of the created post. - /// Thrown when is null. + /// Thrown when is null. + /// Thrown when is of invalid format. /// Thrown when timeline does not exist. /// Thrown if user of does not exist. /// Thrown if data is not a image. Validated by . - Task CreateImagePost(long timelineId, long authorId, byte[] imageData, TimelinePostCommonProperties? properties = null); + Task CreatePost(long timelineId, long authorId, TimelinePostCreateRequest request); /// /// Delete a post. @@ -284,22 +309,27 @@ namespace Timeline.Services }; } - private async Task GeneralCreatePost(long timelineId, long authorId, TimelinePostCommonProperties? properties, Func saveContent) + public async Task CreatePost(long timelineId, long authorId, TimelinePostCreateRequest request) { - if (properties is not null) + if (request is null) + throw new ArgumentNullException(nameof(request)); + + + if (request.Content is null) + throw new ArgumentException("Content is null.", nameof(request)); + { - if (!_colorValidator.Validate(properties.Color, out var message)) - { - throw new ArgumentException(message, nameof(properties)); - } - properties.Time = properties.Time?.MyToUtc(); + if (!_colorValidator.Validate(request.Color, out var message)) + throw new ArgumentException("Color is not valid.", nameof(request)); } + request.Time = request.Time?.MyToUtc(); + await CheckTimelineExistence(timelineId); await CheckUserExistence(authorId); var currentTime = _clock.GetCurrentTime(); - var finalTime = properties?.Time ?? currentTime; + var finalTime = request.Time ?? currentTime; var postEntity = new TimelinePostEntity { @@ -307,10 +337,29 @@ namespace Timeline.Services TimelineId = timelineId, Time = finalTime, LastUpdated = currentTime, - Color = properties?.Color + Color = request.Color + }; + + switch (request.Content) + { + case TimelinePostCreateRequestTextContent content: + postEntity.ContentType = content.TypeName; + postEntity.Content = content.Text; + break; + case TimelinePostCreateRequestImageContent content: + var imageFormat = await _imageValidator.Validate(content.Data); + var imageFormatText = imageFormat.DefaultMimeType; + + var tag = await _dataManager.RetainEntry(content.Data); + + postEntity.ContentType = content.TypeName; + postEntity.Content = tag; + postEntity.ExtraContent = imageFormatText; + break; + default: + throw new ArgumentException("Unknown content type.", nameof(request)); }; - await saveContent(postEntity); var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).SingleAsync(); timelineEntity.CurrentPostLocalId += 1; @@ -323,40 +372,6 @@ namespace Timeline.Services return postEntity; } - public async Task CreateTextPost(long timelineId, long authorId, string text, TimelinePostCommonProperties? properties = null) - { - if (text is null) - throw new ArgumentNullException(nameof(text)); - - return await GeneralCreatePost(timelineId, authorId, properties, (entity) => - { - entity.ContentType = TimelinePostContentTypes.Text; - entity.Content = text; - - return Task.CompletedTask; - }); - } - - public async Task CreateImagePost(long timelineId, long authorId, byte[] data, TimelinePostCommonProperties? properties = null) - { - if (data is null) - throw new ArgumentNullException(nameof(data)); - - await CheckTimelineExistence(timelineId); - - return await GeneralCreatePost(timelineId, authorId, properties, async (entity) => - { - var imageFormat = await _imageValidator.Validate(data); - var imageFormatText = imageFormat.DefaultMimeType; - - var tag = await _dataManager.RetainEntry(data); - - entity.ContentType = TimelinePostContentTypes.Image; - entity.Content = tag; - entity.ExtraContent = imageFormatText; - }); - } - public async Task DeletePost(long timelineId, long postId) { await CheckTimelineExistence(timelineId); -- cgit v1.2.3