aboutsummaryrefslogtreecommitdiff
path: root/BackEnd/Timeline
diff options
context:
space:
mode:
Diffstat (limited to 'BackEnd/Timeline')
-rw-r--r--BackEnd/Timeline/Controllers/TimelinePostController.cs158
-rw-r--r--BackEnd/Timeline/Filters/CatchTimelineNotExistExceptionAttribute.cs (renamed from BackEnd/Timeline/Filters/Timeline.cs)2
-rw-r--r--BackEnd/Timeline/Filters/CatchTimelinePostDataNotExistExceptionAttribute.cs24
-rw-r--r--BackEnd/Timeline/Filters/CatchTimelinePostNotExistExceptionAttribute.cs24
-rw-r--r--BackEnd/Timeline/Formatters/ByteDataInputFormatter.cs (renamed from BackEnd/Timeline/Formatters/BytesInputFormatter.cs)19
-rw-r--r--BackEnd/Timeline/Migrations/20200312112552_AddImagePost.cs2
-rw-r--r--BackEnd/Timeline/Models/Http/ErrorResponse.cs11
-rw-r--r--BackEnd/Timeline/Models/Http/HttpTimelinePostCreateRequest.cs17
-rw-r--r--BackEnd/Timeline/Models/Http/HttpTimelinePostCreateRequestData.cs19
-rw-r--r--BackEnd/Timeline/Models/MimeTypes.cs14
-rw-r--r--BackEnd/Timeline/Models/TimelinePostContentTypes.cs13
-rw-r--r--BackEnd/Timeline/Models/Validation/TimelinePostContentTypeValidator.cs19
-rw-r--r--BackEnd/Timeline/Services/Exceptions/TimelinePostNoDataException.cs15
-rw-r--r--BackEnd/Timeline/Services/TimelinePostCreateDataException.cs16
-rw-r--r--BackEnd/Timeline/Services/TimelinePostDataNotExistException.cs15
-rw-r--r--BackEnd/Timeline/Services/TimelinePostService.cs68
-rw-r--r--BackEnd/Timeline/Startup.cs2
17 files changed, 206 insertions, 232 deletions
diff --git a/BackEnd/Timeline/Controllers/TimelinePostController.cs b/BackEnd/Timeline/Controllers/TimelinePostController.cs
index a0fd1687..06082f0f 100644
--- a/BackEnd/Timeline/Controllers/TimelinePostController.cs
+++ b/BackEnd/Timeline/Controllers/TimelinePostController.cs
@@ -4,14 +4,14 @@ using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using System.ComponentModel.DataAnnotations;
using Timeline.Filters;
-using Timeline.Helpers;
+using Timeline.Helpers.Cache;
using Timeline.Models;
using Timeline.Models.Http;
using Timeline.Models.Mapper;
using Timeline.Models.Validation;
using Timeline.Services;
-using Timeline.Services.Exceptions;
namespace Timeline.Controllers
{
@@ -21,6 +21,8 @@ namespace Timeline.Controllers
[ApiController]
[Route("timelines/{timeline}/posts")]
[CatchTimelineNotExistException]
+ [CatchTimelinePostNotExistException]
+ [CatchTimelinePostDataNotExistException]
[ProducesErrorResponseType(typeof(CommonResponse))]
public class TimelinePostController : Controller
{
@@ -86,57 +88,27 @@ namespace Timeline.Controllers
return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
- try
- {
- var post = await _postService.GetPost(timelineId, postId);
- var result = await _timelineMapper.MapToHttp(post, timeline, Url);
- return result;
- }
- catch (TimelinePostNotExistException)
- {
- return NotFound(ErrorResponse.TimelineController.PostNotExist());
- }
+ var post = await _postService.GetPost(timelineId, postId);
+ var result = await _timelineMapper.MapToHttp(post, timeline, Url);
+ return result;
}
/// <summary>
- /// Get the data of a post. Usually a image post.
+ /// Get the first data of a post.
/// </summary>
/// <param name="timeline">Timeline name.</param>
/// <param name="post">The id of the post.</param>
- /// <param name="ifNoneMatch">If-None-Match header.</param>
/// <returns>The data.</returns>
[HttpGet("{post}/data")]
- [Produces("image/png", "image/jpeg", "image/gif", "image/webp", "application/json", "text/json")]
+ [Produces(MimeTypes.ImagePng, MimeTypes.ImageJpeg, MimeTypes.ImageGif, MimeTypes.ImageWebp, MimeTypes.TextPlain, MimeTypes.TextMarkdown, MimeTypes.TextPlain, MimeTypes.ApplicationJson)]
[ProducesResponseType(typeof(byte[]), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(void), StatusCodes.Status304NotModified)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<IActionResult> DataIndexGet([FromRoute][GeneralTimelineName] string timeline, [FromRoute] long post, [FromHeader(Name = "If-None-Match")] string? ifNoneMatch)
+ public async Task<ActionResult<ByteData>> DataIndexGet([FromRoute][GeneralTimelineName] string timeline, [FromRoute] long post)
{
- _ = ifNoneMatch;
-
- var timelineId = await _timelineService.GetTimelineIdByName(timeline);
-
- if (!UserHasAllTimelineManagementPermission && !await _timelineService.HasReadPermission(timelineId, this.GetOptionalUserId()))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- return await DataCacheHelper.GenerateActionResult(this,
- () => _postService.GetPostDataETag(timelineId, post),
- async () => await _postService.GetPostData(timelineId, post));
- }
- catch (TimelinePostNotExistException)
- {
- return NotFound(ErrorResponse.TimelineController.PostNotExist());
- }
- catch (TimelinePostNoDataException)
- {
- return BadRequest(ErrorResponse.TimelineController.PostNoData());
- }
+ return await DataGet(timeline, post, 0);
}
/// <summary>
@@ -144,17 +116,28 @@ namespace Timeline.Controllers
/// </summary>
/// <param name="timeline">Timeline name.</param>
/// <param name="post">The id of the post.</param>
- /// <param name="ifNoneMatch">If-None-Match header.</param>
+ /// <param name="dataIndex">Index of the data.</param>
/// <returns>The data.</returns>
[HttpGet("{post}/data/{data_index}")]
- [Produces("image/png", "image/jpeg", "image/gif", "image/webp", "application/json", "text/json")]
+ [Produces(MimeTypes.ImagePng, MimeTypes.ImageJpeg, MimeTypes.ImageGif, MimeTypes.ImageWebp, MimeTypes.TextPlain, MimeTypes.TextMarkdown, MimeTypes.TextPlain, MimeTypes.ApplicationJson)]
[ProducesResponseType(typeof(byte[]), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(void), StatusCodes.Status304NotModified)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<IActionResult> DataGet([FromRoute][GeneralTimelineName] string timeline, [FromRoute] long post, [FromHeader(Name = "If-None-Match")] string? ifNoneMatch)
+ public async Task<ActionResult> DataGet([FromRoute][GeneralTimelineName] string timeline, [FromRoute] long post, [FromRoute(Name = "data_index")][Range(0, 100)] long dataIndex)
{
+ var timelineId = await _timelineService.GetTimelineIdByName(timeline);
+
+ if (!UserHasAllTimelineManagementPermission && !await _timelineService.HasReadPermission(timelineId, this.GetOptionalUserId()))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
+ }
+
+ return await DataCacheHelper.GenerateActionResult(this,
+ () => _postService.GetPostDataDigest(timelineId, post, dataIndex),
+ () => _postService.GetPostData(timelineId, post, dataIndex)
+ );
}
/// <summary>
@@ -179,50 +162,36 @@ namespace Timeline.Controllers
return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
- var requestContent = body.Content;
-
- TimelinePostCreateRequestData createContent;
-
- switch (requestContent.Type)
+ var createRequest = new TimelinePostCreateRequest()
{
- case TimelinePostDataKind.Text:
- if (requestContent.Text is null)
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_TextContentTextRequired));
- }
- createContent = new TimelinePostCreateRequestTextContent(requestContent.Text);
- break;
- case TimelinePostDataKind.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 TimelinePostCreateRequestImageData(data);
- break;
- default:
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ContentUnknownType));
+ Time = body.Time,
+ Color = body.Color
+ };
+ for (int i = 0; i < body.DataList.Count; i++)
+ {
+ var data = body.DataList[i];
+ try
+ {
+ var d = Convert.FromBase64String(data.Data);
+ createRequest.DataList.Add(new TimelinePostCreateRequestData(data.ContentType, d));
+ }
+ catch (FormatException)
+ {
+ return BadRequest(new CommonResponse(ErrorCodes.Common.InvalidModel, $"Data at index {i} is not a valid base64 string."));
+ }
}
+
try
{
- var post = await _postService.CreatePost(timelineId, userId, new TimelinePostCreateRequest(createContent) { Time = body.Time, Color = body.Color });
+ var post = await _postService.CreatePost(timelineId, userId, createRequest);
var result = await _timelineMapper.MapToHttp(post, timeline, Url);
return result;
}
- catch (ImageException)
+ catch (TimelinePostCreateDataException e)
{
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataNotImage));
+ return BadRequest(new CommonResponse(ErrorCodes.Common.InvalidModel, $"Data at index {e.Index} is invalid. {e.Message}"));
}
}
@@ -243,21 +212,15 @@ namespace Timeline.Controllers
{
var timelineId = await _timelineService.GetTimelineIdByName(timeline);
- try
+ if (!UserHasAllTimelineManagementPermission && !await _postService.HasPostModifyPermission(timelineId, post, this.GetUserId(), true))
{
- if (!UserHasAllTimelineManagementPermission && !await _postService.HasPostModifyPermission(timelineId, post, this.GetUserId(), true))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- var entity = await _postService.PatchPost(timelineId, post, new TimelinePostPatchRequest { Time = body.Time, Color = body.Color });
- var result = await _timelineMapper.MapToHttp(entity, timeline, Url);
- return Ok(result);
- }
- catch (TimelinePostNotExistException)
- {
- return BadRequest(ErrorResponse.TimelineController.PostNotExist());
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
+
+ var entity = await _postService.PatchPost(timelineId, post, new TimelinePostPatchRequest { Time = body.Time, Color = body.Color });
+ var result = await _timelineMapper.MapToHttp(entity, timeline, Url);
+
+ return Ok(result);
}
/// <summary>
@@ -276,19 +239,14 @@ namespace Timeline.Controllers
{
var timelineId = await _timelineService.GetTimelineIdByName(timeline);
- try
+ if (!UserHasAllTimelineManagementPermission && !await _postService.HasPostModifyPermission(timelineId, post, this.GetUserId(), true))
{
- if (!UserHasAllTimelineManagementPermission && !await _postService.HasPostModifyPermission(timelineId, post, this.GetUserId(), true))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
- await _postService.DeletePost(timelineId, post);
- return Ok();
- }
- catch (TimelinePostNotExistException)
- {
- return BadRequest(ErrorResponse.TimelineController.PostNotExist());
+ return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
}
+
+ await _postService.DeletePost(timelineId, post);
+
+ return Ok();
}
}
}
diff --git a/BackEnd/Timeline/Filters/Timeline.cs b/BackEnd/Timeline/Filters/CatchTimelineNotExistExceptionAttribute.cs
index 6a730ee7..857d1d2b 100644
--- a/BackEnd/Timeline/Filters/Timeline.cs
+++ b/BackEnd/Timeline/Filters/CatchTimelineNotExistExceptionAttribute.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Timeline.Models.Http;
diff --git a/BackEnd/Timeline/Filters/CatchTimelinePostDataNotExistExceptionAttribute.cs b/BackEnd/Timeline/Filters/CatchTimelinePostDataNotExistExceptionAttribute.cs
new file mode 100644
index 00000000..8b5868aa
--- /dev/null
+++ b/BackEnd/Timeline/Filters/CatchTimelinePostDataNotExistExceptionAttribute.cs
@@ -0,0 +1,24 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Timeline.Models.Http;
+using Timeline.Services;
+
+namespace Timeline.Filters
+{
+ public class CatchTimelinePostDataNotExistExceptionAttribute : ExceptionFilterAttribute
+ {
+ public override void OnException(ExceptionContext context)
+ {
+ const string message = "Timeline post data does not exist.";
+
+ if (context.Exception is TimelinePostDataNotExistException e)
+ {
+ if (HttpMethods.IsGet(context.HttpContext.Request.Method))
+ context.Result = new NotFoundObjectResult(new CommonResponse(ErrorCodes.TimelineController.PostNotExist, message));
+ else
+ context.Result = new BadRequestObjectResult(new CommonResponse(ErrorCodes.TimelineController.PostNotExist, message));
+ }
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Filters/CatchTimelinePostNotExistExceptionAttribute.cs b/BackEnd/Timeline/Filters/CatchTimelinePostNotExistExceptionAttribute.cs
new file mode 100644
index 00000000..ac3789c7
--- /dev/null
+++ b/BackEnd/Timeline/Filters/CatchTimelinePostNotExistExceptionAttribute.cs
@@ -0,0 +1,24 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Timeline.Models.Http;
+using Timeline.Services.Exceptions;
+
+namespace Timeline.Filters
+{
+ public class CatchTimelinePostNotExistExceptionAttribute : ExceptionFilterAttribute
+ {
+ public override void OnException(ExceptionContext context)
+ {
+ const string message = "Timeline post does not exist.";
+
+ if (context.Exception is TimelinePostNotExistException e)
+ {
+ if (HttpMethods.IsGet(context.HttpContext.Request.Method))
+ context.Result = new NotFoundObjectResult(new CommonResponse(ErrorCodes.TimelineController.PostNotExist, message));
+ else
+ context.Result = new BadRequestObjectResult(new CommonResponse(ErrorCodes.TimelineController.PostNotExist, message));
+ }
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Formatters/BytesInputFormatter.cs b/BackEnd/Timeline/Formatters/ByteDataInputFormatter.cs
index ac6537c9..2451ead6 100644
--- a/BackEnd/Timeline/Formatters/BytesInputFormatter.cs
+++ b/BackEnd/Timeline/Formatters/ByteDataInputFormatter.cs
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
using System;
using System.Threading.Tasks;
using Timeline.Models;
@@ -9,19 +8,21 @@ using Timeline.Models;
namespace Timeline.Formatters
{
/// <summary>
- /// Formatter that reads body as bytes.
+ /// Formatter that reads body as byte data.
/// </summary>
- public class BytesInputFormatter : InputFormatter
+ public class ByteDataInputFormatter : InputFormatter
{
/// <summary>
///
/// </summary>
- public BytesInputFormatter()
+ public ByteDataInputFormatter()
{
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/png"));
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpeg"));
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/gif"));
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/webp"));
+ SupportedMediaTypes.Add(MimeTypes.ImagePng);
+ SupportedMediaTypes.Add(MimeTypes.ImageJpeg);
+ SupportedMediaTypes.Add(MimeTypes.ImageGif);
+ SupportedMediaTypes.Add(MimeTypes.ImageWebp);
+ SupportedMediaTypes.Add(MimeTypes.TextPlain);
+ SupportedMediaTypes.Add(MimeTypes.TextMarkdown);
}
/// <inheritdoc/>
@@ -41,7 +42,7 @@ namespace Timeline.Formatters
var request = context.HttpContext.Request;
var contentLength = request.ContentLength;
- var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<BytesInputFormatter>>();
+ var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ByteDataInputFormatter>>();
if (contentLength == null)
{
diff --git a/BackEnd/Timeline/Migrations/20200312112552_AddImagePost.cs b/BackEnd/Timeline/Migrations/20200312112552_AddImagePost.cs
index b6cc29a3..7d9c6614 100644
--- a/BackEnd/Timeline/Migrations/20200312112552_AddImagePost.cs
+++ b/BackEnd/Timeline/Migrations/20200312112552_AddImagePost.cs
@@ -20,7 +20,7 @@ namespace Timeline.Migrations
migrationBuilder.Sql($@"
UPDATE timeline_posts
-SET content_type = '{TimelinePostDataKind.Text}';
+SET content_type = 'text';
");
}
diff --git a/BackEnd/Timeline/Models/Http/ErrorResponse.cs b/BackEnd/Timeline/Models/Http/ErrorResponse.cs
index 1bc46680..3812471d 100644
--- a/BackEnd/Timeline/Models/Http/ErrorResponse.cs
+++ b/BackEnd/Timeline/Models/Http/ErrorResponse.cs
@@ -253,17 +253,6 @@ namespace Timeline.Models.Http
{
return new CommonResponse(ErrorCodes.TimelineController.PostNotExist, string.Format(message, formatArgs));
}
-
- public static CommonResponse PostNoData(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.PostNoData, string.Format(TimelineController_PostNoData, formatArgs));
- }
-
- public static CommonResponse CustomMessage_PostNoData(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.PostNoData, string.Format(message, formatArgs));
- }
-
}
}
diff --git a/BackEnd/Timeline/Models/Http/HttpTimelinePostCreateRequest.cs b/BackEnd/Timeline/Models/Http/HttpTimelinePostCreateRequest.cs
index 20d1a25b..07d823ad 100644
--- a/BackEnd/Timeline/Models/Http/HttpTimelinePostCreateRequest.cs
+++ b/BackEnd/Timeline/Models/Http/HttpTimelinePostCreateRequest.cs
@@ -5,22 +5,6 @@ using Timeline.Models.Validation;
namespace Timeline.Models.Http
{
- public class HttpTimelinePostCreateRequestData
- {
- /// <summary>
- /// Kind of the data.
- /// </summary>
- [Required]
- [TimelinePostDataKind]
- public string Kind { get; set; } = default!;
-
- /// <summary>
- /// The true data. If kind is text or markdown, this is a string. If kind is image, this is base64 of data.
- /// </summary>
- [Required]
- public string Data { get; set; } = default!;
- }
-
public class HttpTimelinePostCreateRequest
{
/// <summary>
@@ -28,6 +12,7 @@ namespace Timeline.Models.Http
/// </summary>
[Required]
[MinLength(1)]
+ [MaxLength(100)]
public List<HttpTimelinePostCreateRequestData> DataList { get; set; } = default!;
/// <summary>
diff --git a/BackEnd/Timeline/Models/Http/HttpTimelinePostCreateRequestData.cs b/BackEnd/Timeline/Models/Http/HttpTimelinePostCreateRequestData.cs
new file mode 100644
index 00000000..94ee5aa7
--- /dev/null
+++ b/BackEnd/Timeline/Models/Http/HttpTimelinePostCreateRequestData.cs
@@ -0,0 +1,19 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Timeline.Models.Http
+{
+ public class HttpTimelinePostCreateRequestData
+ {
+ /// <summary>
+ /// Mime type of the data.
+ /// </summary>
+ [Required]
+ public string ContentType { get; set; } = default!;
+
+ /// <summary>
+ /// Base64 of data.
+ /// </summary>
+ [Required]
+ public string Data { get; set; } = default!;
+ }
+}
diff --git a/BackEnd/Timeline/Models/MimeTypes.cs b/BackEnd/Timeline/Models/MimeTypes.cs
new file mode 100644
index 00000000..37d3a893
--- /dev/null
+++ b/BackEnd/Timeline/Models/MimeTypes.cs
@@ -0,0 +1,14 @@
+namespace Timeline.Models
+{
+ public static class MimeTypes
+ {
+ public const string ImagePng = "image/png";
+ public const string ImageJpeg = "image/jpeg";
+ public const string ImageGif = "image/gif";
+ public const string ImageWebp = "image/webp";
+ public const string TextPlain = "text/plain";
+ public const string TextMarkdown = "text/markdown";
+ public const string TextJson = "text/json";
+ public const string ApplicationJson = "application/json";
+ }
+}
diff --git a/BackEnd/Timeline/Models/TimelinePostContentTypes.cs b/BackEnd/Timeline/Models/TimelinePostContentTypes.cs
deleted file mode 100644
index d432e03c..00000000
--- a/BackEnd/Timeline/Models/TimelinePostContentTypes.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System.Collections.Generic;
-
-namespace Timeline.Models
-{
- public static class TimelinePostDataKind
- {
- public static IReadOnlyList<string> AllTypes { get; } = new List<string> { Text, Image, Markdown };
-
- public const string Text = "text";
- public const string Image = "image";
- public const string Markdown = "markdown";
- }
-}
diff --git a/BackEnd/Timeline/Models/Validation/TimelinePostContentTypeValidator.cs b/BackEnd/Timeline/Models/Validation/TimelinePostContentTypeValidator.cs
deleted file mode 100644
index b65c846c..00000000
--- a/BackEnd/Timeline/Models/Validation/TimelinePostContentTypeValidator.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-using System.Linq;
-
-namespace Timeline.Models.Validation
-{
- public class TimelinePostDataKindValidator : StringSetValidator
- {
- public TimelinePostDataKindValidator() : base(TimelinePostDataKind.AllTypes.ToArray()) { }
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
- public class TimelinePostDataKindAttribute : ValidateWithAttribute
- {
- public TimelinePostDataKindAttribute() : base(typeof(TimelinePostDataKindValidator))
- {
-
- }
- }
-}
diff --git a/BackEnd/Timeline/Services/Exceptions/TimelinePostNoDataException.cs b/BackEnd/Timeline/Services/Exceptions/TimelinePostNoDataException.cs
deleted file mode 100644
index c4b6bf62..00000000
--- a/BackEnd/Timeline/Services/Exceptions/TimelinePostNoDataException.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-
-namespace Timeline.Services.Exceptions
-{
- [Serializable]
- public class TimelinePostNoDataException : Exception
- {
- public TimelinePostNoDataException() : this(null, null) { }
- public TimelinePostNoDataException(string? message) : this(message, null) { }
- public TimelinePostNoDataException(string? message, Exception? inner) : base(Resources.Services.Exceptions.TimelineNoDataException.AppendAdditionalMessage(message), inner) { }
- protected TimelinePostNoDataException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
-}
diff --git a/BackEnd/Timeline/Services/TimelinePostCreateDataException.cs b/BackEnd/Timeline/Services/TimelinePostCreateDataException.cs
new file mode 100644
index 00000000..fd1e6664
--- /dev/null
+++ b/BackEnd/Timeline/Services/TimelinePostCreateDataException.cs
@@ -0,0 +1,16 @@
+namespace Timeline.Services
+{
+ [System.Serializable]
+ public class TimelinePostCreateDataException : System.Exception
+ {
+ public TimelinePostCreateDataException() { }
+ public TimelinePostCreateDataException(string message) : base(message) { }
+ public TimelinePostCreateDataException(string message, System.Exception inner) : base(message, inner) { }
+ public TimelinePostCreateDataException(long index, string? message, System.Exception? inner = null) : base(message, inner) { Index = index; }
+ protected TimelinePostCreateDataException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
+
+ public long Index { get; }
+ }
+}
diff --git a/BackEnd/Timeline/Services/TimelinePostDataNotExistException.cs b/BackEnd/Timeline/Services/TimelinePostDataNotExistException.cs
new file mode 100644
index 00000000..eac7a771
--- /dev/null
+++ b/BackEnd/Timeline/Services/TimelinePostDataNotExistException.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Timeline.Services
+{
+ [Serializable]
+ public class TimelinePostDataNotExistException : Exception
+ {
+ public TimelinePostDataNotExistException() : this(null, null) { }
+ public TimelinePostDataNotExistException(string? message) : this(message, null) { }
+ public TimelinePostDataNotExistException(string? message, Exception? inner) : base(message, inner) { }
+ protected TimelinePostDataNotExistException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/BackEnd/Timeline/Services/TimelinePostService.cs b/BackEnd/Timeline/Services/TimelinePostService.cs
index 98841478..cea702a1 100644
--- a/BackEnd/Timeline/Services/TimelinePostService.cs
+++ b/BackEnd/Timeline/Services/TimelinePostService.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading.Tasks;
using Timeline.Entities;
using Timeline.Helpers;
+using Timeline.Helpers.Cache;
using Timeline.Models;
using Timeline.Models.Validation;
using Timeline.Services.Exceptions;
@@ -14,49 +15,15 @@ using static Timeline.Resources.Services.TimelineService;
namespace Timeline.Services
{
- public class TimelinePostDataDigest
- {
- public TimelinePostDataDigest(string kind, string eTag, DateTime lastModified)
- {
- Kind = kind;
- ETag = eTag;
- LastModified = lastModified;
- }
-
- public string Kind { get; set; }
- public string ETag { get; set; }
- public DateTime LastModified { get; set; }
- }
-
- public class TimelinePostData
- {
- public TimelinePostData(string kind, byte[] data, string eTag, DateTime lastModified)
- {
- Kind = kind;
- Data = data;
- ETag = eTag;
- LastModified = lastModified;
- }
-
- public string Kind { 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 string ETag { get; set; }
- public DateTime LastModified { get; set; }
- }
-
public class TimelinePostCreateRequestData
{
- public TimelinePostCreateRequestData(string kind, byte[] data)
+ public TimelinePostCreateRequestData(string contentType, byte[] data)
{
- Kind = kind;
+ ContentType = contentType;
Data = data;
}
- public string Kind { get; set; }
+ 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
@@ -69,14 +36,13 @@ namespace Timeline.Services
/// <summary>If not set, current time is used.</summary>
public DateTime? Time { get; set; }
- public List<TimelinePostCreateRequestData> Content { get; set; } = new List<TimelinePostCreateRequestData>();
+ public List<TimelinePostCreateRequestData> DataList { get; set; } = new List<TimelinePostCreateRequestData>();
}
public class TimelinePostPatchRequest
{
public string? Color { get; set; }
public DateTime? Time { get; set; }
- public List<TimelinePostCreateRequestData?>? Content { get; set; }
}
public interface ITimelinePostService
@@ -102,18 +68,29 @@ namespace Timeline.Services
/// <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 includeDelete = false);
- Task<TimelinePostDataDigest> GetPostDataDigest(long timelineId, long postId, long dataIndex);
+ /// <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 id of the timeline of the post.</param>
- /// <param name="postId">The id of the post.</param>
- /// <returns>The etag of the data.</returns>
+ /// <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="TimelinePostNoDataException">Thrown when post has no data.</exception>
- Task<TimelinePostData> GetPostData(long timelineId, long postId, long dataIndex);
+ /// <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.
@@ -140,7 +117,6 @@ namespace Timeline.Services
/// <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>
- /// <exception cref="ImageException">Thrown if data is not a image. Validated by <see cref="ImageValidator"/>.</exception>
Task<TimelinePostEntity> PatchPost(long timelineId, long postId, TimelinePostPatchRequest request);
/// <summary>
diff --git a/BackEnd/Timeline/Startup.cs b/BackEnd/Timeline/Startup.cs
index 0fab798b..5951dc80 100644
--- a/BackEnd/Timeline/Startup.cs
+++ b/BackEnd/Timeline/Startup.cs
@@ -61,7 +61,7 @@ namespace Timeline
services.AddControllers(setup =>
{
setup.InputFormatters.Add(new StringInputFormatter());
- setup.InputFormatters.Add(new BytesInputFormatter());
+ setup.InputFormatters.Add(new ByteDataInputFormatter());
setup.Filters.Add(new ConsumesAttribute(MediaTypeNames.Application.Json, "text/json"));
setup.Filters.Add(new ProducesAttribute(MediaTypeNames.Application.Json, "text/json"));
setup.UseApiRoutePrefix("api");