aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-07-12 17:19:04 +0800
committerGitHub <noreply@github.com>2020-07-12 17:19:04 +0800
commit5b95c1f32db4daa91f70b8e586f7d4ce3b9956d2 (patch)
tree9aa60f83185e84a5ab5fb07d9957f2ea539ecd63
parent15e7467e6089537f0f3e2290f14a99b8a1fc2d76 (diff)
parent8b1d160af7ae206690bbda12ec4c862e40f67287 (diff)
downloadtimeline-5b95c1f32db4daa91f70b8e586f7d4ce3b9956d2.tar.gz
timeline-5b95c1f32db4daa91f70b8e586f7d4ce3b9956d2.tar.bz2
timeline-5b95c1f32db4daa91f70b8e586f7d4ce3b9956d2.zip
Merge pull request #116 from crupest/fix-114
Back-end: Timleine If-Modified-Since and checkUniqueId.
-rw-r--r--Timeline.Tests/IntegratedTests/TimelineTest.cs75
-rw-r--r--Timeline.Tests/Services/TimelineServiceTest.cs30
-rw-r--r--Timeline/Controllers/TimelineController.cs47
-rw-r--r--Timeline/Services/TimelineService.cs50
4 files changed, 198 insertions, 4 deletions
diff --git a/Timeline.Tests/IntegratedTests/TimelineTest.cs b/Timeline.Tests/IntegratedTests/TimelineTest.cs
index ba335bd6..49672f29 100644
--- a/Timeline.Tests/IntegratedTests/TimelineTest.cs
+++ b/Timeline.Tests/IntegratedTests/TimelineTest.cs
@@ -1332,5 +1332,80 @@ namespace Timeline.Tests.IntegratedTests
posts.Select(p => p.Content == null).Should().Equal(false, true, false);
}
}
+
+ [Theory]
+ [MemberData(nameof(TimelineUrlGeneratorData))]
+ public async Task Timeline_Get_IfModifiedSince_And_CheckUniqueId(TimelineUrlGenerator urlGenerator)
+ {
+ using var client = await CreateClientAsUser();
+
+ DateTime lastModifiedTime;
+ TimelineInfo timeline;
+ string uniqueId;
+
+ {
+ var res = await client.GetAsync(urlGenerator(1));
+ var body = res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>().Which;
+ timeline = body;
+ lastModifiedTime = body.LastModified;
+ uniqueId = body.UniqueId;
+ }
+
+ {
+ using var req = new HttpRequestMessage
+ {
+ RequestUri = new Uri(client.BaseAddress, urlGenerator(1)),
+ Method = HttpMethod.Get,
+ };
+ req.Headers.IfModifiedSince = lastModifiedTime.AddSeconds(1);
+ var res = await client.SendAsync(req);
+ res.Should().HaveStatusCode(304);
+ }
+
+ {
+ using var req = new HttpRequestMessage
+ {
+ RequestUri = new Uri(client.BaseAddress, urlGenerator(1)),
+ Method = HttpMethod.Get,
+ };
+ req.Headers.IfModifiedSince = lastModifiedTime.AddSeconds(-1);
+ var res = await client.SendAsync(req);
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>()
+ .Which.Should().BeEquivalentTo(timeline);
+ }
+
+ {
+ var res = await client.GetAsync(urlGenerator(1, null,
+ new Dictionary<string, string> { { "ifModifiedSince", lastModifiedTime.AddSeconds(1).ToString("s", CultureInfo.InvariantCulture) } }));
+ res.Should().HaveStatusCode(304);
+ }
+
+ {
+ var res = await client.GetAsync(urlGenerator(1, null,
+ new Dictionary<string, string> { { "ifModifiedSince", lastModifiedTime.AddSeconds(-1).ToString("s", CultureInfo.InvariantCulture) } }));
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>()
+ .Which.Should().BeEquivalentTo(timeline);
+ }
+
+ {
+ var res = await client.GetAsync(urlGenerator(1, null,
+ new Dictionary<string, string> { { "ifModifiedSince", lastModifiedTime.AddSeconds(1).ToString("s", CultureInfo.InvariantCulture) },
+ {"checkUniqueId", uniqueId } }));
+ res.Should().HaveStatusCode(304);
+ }
+
+ {
+ var testUniqueId = (uniqueId[0] == 'a' ? "b" : "a") + uniqueId[1..];
+ var res = await client.GetAsync(urlGenerator(1, null,
+ new Dictionary<string, string> { { "ifModifiedSince", lastModifiedTime.AddSeconds(1).ToString("s", CultureInfo.InvariantCulture) },
+ {"checkUniqueId", testUniqueId } }));
+ res.Should().HaveStatusCode(200)
+ .And.HaveJsonBody<TimelineInfo>()
+ .Which.Should().BeEquivalentTo(timeline);
+ }
+ }
}
}
diff --git a/Timeline.Tests/Services/TimelineServiceTest.cs b/Timeline.Tests/Services/TimelineServiceTest.cs
index 123c2b7f..919400a3 100644
--- a/Timeline.Tests/Services/TimelineServiceTest.cs
+++ b/Timeline.Tests/Services/TimelineServiceTest.cs
@@ -59,6 +59,36 @@ namespace Timeline.Tests.Services
[Theory]
[InlineData("@user")]
[InlineData("tl")]
+ public async Task Timeline_GetLastModified(string timelineName)
+ {
+ var time = _clock.ForwardCurrentTime();
+
+ var _ = TimelineHelper.ExtractTimelineName(timelineName, out var isPersonal);
+ if (!isPersonal)
+ await _timelineService.CreateTimeline(timelineName, await _userService.GetUserIdByUsername("user"));
+
+ var t = await _timelineService.GetTimelineLastModifiedTime(timelineName);
+
+ t.Should().Be(time);
+ }
+
+ [Theory]
+ [InlineData("@user")]
+ [InlineData("tl")]
+ public async Task Timeline_GetUnqiueId(string timelineName)
+ {
+ var _ = TimelineHelper.ExtractTimelineName(timelineName, out var isPersonal);
+ if (!isPersonal)
+ await _timelineService.CreateTimeline(timelineName, await _userService.GetUserIdByUsername("user"));
+
+ var uniqueId = await _timelineService.GetTimelineUniqueId(timelineName);
+
+ uniqueId.Should().NotBeNullOrEmpty();
+ }
+
+ [Theory]
+ [InlineData("@user")]
+ [InlineData("tl")]
public async Task Timeline_LastModified(string timelineName)
{
var initTime = _clock.ForwardCurrentTime();
diff --git a/Timeline/Controllers/TimelineController.cs b/Timeline/Controllers/TimelineController.cs
index 2330698f..72404ea3 100644
--- a/Timeline/Controllers/TimelineController.cs
+++ b/Timeline/Controllers/TimelineController.cs
@@ -94,11 +94,50 @@ namespace Timeline.Controllers
}
[HttpGet("timelines/{name}")]
- public async Task<ActionResult<TimelineInfo>> TimelineGet([FromRoute][GeneralTimelineName] string name)
+ public async Task<ActionResult<TimelineInfo>> TimelineGet([FromRoute][GeneralTimelineName] string name, [FromQuery] string? checkUniqueId, [FromQuery(Name = "ifModifiedSince")] DateTime? queryIfModifiedSince, [FromHeader(Name = "If-Modified-Since")] DateTime? headerIfModifiedSince)
{
- var timeline = await _service.GetTimeline(name);
- var result = _mapper.Map<TimelineInfo>(timeline);
- return result;
+ DateTime? ifModifiedSince = null;
+ if (queryIfModifiedSince.HasValue)
+ {
+ ifModifiedSince = queryIfModifiedSince.Value;
+ }
+ else if (headerIfModifiedSince != null)
+ {
+ ifModifiedSince = headerIfModifiedSince.Value;
+ }
+
+ bool returnNotModified = false;
+
+ if (ifModifiedSince.HasValue)
+ {
+ var lastModified = await _service.GetTimelineLastModifiedTime(name);
+ if (lastModified < ifModifiedSince.Value)
+ {
+ if (checkUniqueId != null)
+ {
+ var uniqueId = await _service.GetTimelineUniqueId(name);
+ if (uniqueId == checkUniqueId)
+ {
+ returnNotModified = true;
+ }
+ }
+ else
+ {
+ returnNotModified = true;
+ }
+ }
+ }
+
+ if (returnNotModified)
+ {
+ return StatusCode(StatusCodes.Status304NotModified);
+ }
+ else
+ {
+ var timeline = await _service.GetTimeline(name);
+ var result = _mapper.Map<TimelineInfo>(timeline);
+ return result;
+ }
}
[HttpGet("timelines/{name}/posts")]
diff --git a/Timeline/Services/TimelineService.cs b/Timeline/Services/TimelineService.cs
index a0d72ad3..eafb0088 100644
--- a/Timeline/Services/TimelineService.cs
+++ b/Timeline/Services/TimelineService.cs
@@ -67,6 +67,32 @@ namespace Timeline.Services
public interface ITimelineService
{
/// <summary>
+ /// Get the timeline last modified time (not include name change).
+ /// </summary>
+ /// <param name="timelineName">The name of the timeline.</param>
+ /// <returns>The timeline info.</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<DateTime> GetTimelineLastModifiedTime(string timelineName);
+
+ /// <summary>
+ /// Get the timeline unique id.
+ /// </summary>
+ /// <param name="timelineName">The name of the timeline.</param>
+ /// <returns>The timeline info.</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<string> GetTimelineUniqueId(string timelineName);
+
+ /// <summary>
/// Get the timeline info.
/// </summary>
/// <param name="timelineName">The name of the timeline.</param>
@@ -497,6 +523,30 @@ namespace Timeline.Services
}
}
+ public async Task<DateTime> GetTimelineLastModifiedTime(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.LastModified }).SingleAsync();
+
+ return timelineEntity.LastModified;
+ }
+
+ public async Task<string> GetTimelineUniqueId(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.UniqueId }).SingleAsync();
+
+ return timelineEntity.UniqueId;
+ }
+
public async Task<Models.Timeline> GetTimeline(string timelineName)
{
if (timelineName == null)