From eba5936d0ed97d50d4c2d263624c02457539c8c1 Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 16 Jan 2021 18:38:17 +0800 Subject: feat: Add search service. --- BackEnd/Timeline/Services/SearchService.cs | 98 ++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 BackEnd/Timeline/Services/SearchService.cs (limited to 'BackEnd/Timeline') diff --git a/BackEnd/Timeline/Services/SearchService.cs b/BackEnd/Timeline/Services/SearchService.cs new file mode 100644 index 00000000..eac81fd0 --- /dev/null +++ b/BackEnd/Timeline/Services/SearchService.cs @@ -0,0 +1,98 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Timeline.Entities; + +namespace Timeline.Services +{ + public class SearchResultItem + { + public SearchResultItem(TItem item, int score) + { + Item = item; + Score = score; + } + + public TItem Item { get; set; } = default!; + + /// + /// Bigger is better. + /// + public int Score { get; set; } + } + + public class SearchResult + { +#pragma warning disable CA2227 // Collection properties should be read only + public List> Items { get; set; } = new(); +#pragma warning restore CA2227 // Collection properties should be read only + } + + public interface ISearchService + { + /// + /// Search timelines whose name or title contains query string. + /// + /// String to contain. + /// Search results. + /// Thrown when is null. + /// Thrown when is empty. + Task> SearchTimeline(string query); + + /// + /// Search users whose username or nickname contains query string. + /// + /// String to contain. + /// Search results. + /// Thrown when is null. + /// Thrown when is empty. + Task> SearchUser(string query); + } + + public class SearchService : ISearchService + { + private readonly DatabaseContext _database; + + public SearchService(DatabaseContext database) + { + _database = database; + } + + public async Task> SearchTimeline(string query) + { + if (query is null) + throw new ArgumentNullException(nameof(query)); + if (query.Length == 0) + throw new ArgumentException("Query string can't be empty.", nameof(query)); + + var nameLikeTimelines = await _database.Timelines.Include(t => t.Owner).Where(t => t.Name == null ? t.Owner.Username.Contains(query) : t.Name.Contains(query)).ToListAsync(); + var titleLikeTimelines = await _database.Timelines.Where(t => t.Title != null && t.Title.Contains(query)).ToListAsync(); + + var searchResult = new SearchResult(); + searchResult.Items.AddRange(nameLikeTimelines.Select(t => new SearchResultItem(t, 2))); + searchResult.Items.AddRange(titleLikeTimelines.Select(t => new SearchResultItem(t, 1))); + + return searchResult; + } + + public async Task> SearchUser(string query) + { + if (query is null) + throw new ArgumentNullException(nameof(query)); + if (query.Length == 0) + throw new ArgumentException("Query string can't be empty.", nameof(query)); + + var usernameLikeUsers = await _database.Users.Where(u => u.Username.Contains(query)).ToListAsync(); + var nicknameLikeUsers = await _database.Users.Where(u => u.Nickname != null && u.Nickname.Contains(query)).ToListAsync(); + + var searchResult = new SearchResult(); + searchResult.Items.AddRange(usernameLikeUsers.Select(u => new SearchResultItem(u, 2))); + searchResult.Items.AddRange(nicknameLikeUsers.Select(u => new SearchResultItem(u, 1))); + + return searchResult; + + } + } +} -- cgit v1.2.3 From 280698f244bdaf3fbe2896d7104826d132c0b95a Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 16 Jan 2021 19:48:09 +0800 Subject: test: Add unit tests for search service. --- .../Timeline.Tests/Services/DatabaseBasedTest.cs | 55 ----------------- .../Timeline.Tests/Services/SearchServiceTest.cs | 49 +++++++++++++++ BackEnd/Timeline.Tests/Services/ServiceTestBase.cs | 71 ++++++++++++++++++++++ .../Services/UserDeleteServiceTest.cs | 4 +- .../Services/UserPermissionServiceTest.cs | 4 +- BackEnd/Timeline/Services/SearchService.cs | 6 ++ 6 files changed, 130 insertions(+), 59 deletions(-) delete mode 100644 BackEnd/Timeline.Tests/Services/DatabaseBasedTest.cs create mode 100644 BackEnd/Timeline.Tests/Services/SearchServiceTest.cs create mode 100644 BackEnd/Timeline.Tests/Services/ServiceTestBase.cs (limited to 'BackEnd/Timeline') diff --git a/BackEnd/Timeline.Tests/Services/DatabaseBasedTest.cs b/BackEnd/Timeline.Tests/Services/DatabaseBasedTest.cs deleted file mode 100644 index 90fb6463..00000000 --- a/BackEnd/Timeline.Tests/Services/DatabaseBasedTest.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Threading.Tasks; -using Timeline.Entities; -using Timeline.Tests.Helpers; -using Xunit; -using Xunit.Abstractions; - -namespace Timeline.Tests.Services -{ - public abstract class DatabaseBasedTest : IAsyncLifetime - { - protected TestDatabase TestDatabase { get; } - protected DatabaseContext Database { get; private set; } = default!; - - private readonly ITestOutputHelper? _testOutputHelper; - - protected DatabaseBasedTest(bool databaseCreateUsers = true, ITestOutputHelper? testOutputHelper = null) - { - _testOutputHelper = testOutputHelper; - TestDatabase = new TestDatabase(databaseCreateUsers); - } - - protected DatabaseBasedTest(ITestOutputHelper? testOutputHelper) : this(true, testOutputHelper) { } - - public async Task InitializeAsync() - { - await TestDatabase.InitializeAsync(); - Database = TestDatabase.CreateContext(_testOutputHelper); - await OnDatabaseCreatedAsync(); - OnDatabaseCreated(); - } - - public async Task DisposeAsync() - { - BeforeDatabaseDestroy(); - await BeforeDatabaseDestroyAsync(); - await Database.DisposeAsync(); - await TestDatabase.DisposeAsync(); - } - - - protected virtual void OnDatabaseCreated() { } - protected virtual void BeforeDatabaseDestroy() { } - - - protected virtual Task OnDatabaseCreatedAsync() - { - return Task.CompletedTask; - } - - protected virtual Task BeforeDatabaseDestroyAsync() - { - return Task.CompletedTask; - } - } -} diff --git a/BackEnd/Timeline.Tests/Services/SearchServiceTest.cs b/BackEnd/Timeline.Tests/Services/SearchServiceTest.cs new file mode 100644 index 00000000..968352c0 --- /dev/null +++ b/BackEnd/Timeline.Tests/Services/SearchServiceTest.cs @@ -0,0 +1,49 @@ +using FluentAssertions; +using System.Threading.Tasks; +using Timeline.Services; +using Xunit; + +namespace Timeline.Tests.Services +{ + public class SearchServiceTest : ServiceTestBase + { + private SearchService _service = default!; + + protected override void OnInitialize() + { + _service = new SearchService(Database); + } + + [Fact] + public async Task TimelineSearch_Should_Work() + { + await TimelineService.CreateTimeline("hahaha", UserId); + var t2 = await TimelineService.CreateTimeline("bababa", UserId); + await TimelineService.ChangeProperty(t2.Id, new TimelineChangePropertyParams { Title = "hahaha" }); + await TimelineService.CreateTimeline("bbbbbb", UserId); + + var searchResult = await _service.SearchTimeline("hah"); + searchResult.Items.Should().HaveCount(2); + searchResult.Items[0].Item.Name.Should().Be("hahaha"); + searchResult.Items[0].Score.Should().Be(2); + searchResult.Items[1].Item.Name.Should().Be("bababa"); + searchResult.Items[1].Score.Should().Be(1); + } + + [Fact] + public async Task UserSearch_Should_Work() + { + await UserService.CreateUser("hahaha", "p"); + var u2 = await UserService.CreateUser("bababa", "p"); + await UserService.ModifyUser(u2.Id, new ModifyUserParams { Nickname = "hahaha" }); + await UserService.CreateUser("bbbbbb", "p"); + + var searchResult = await _service.SearchUser("hah"); + searchResult.Items.Should().HaveCount(2); + searchResult.Items[0].Item.Username.Should().Be("hahaha"); + searchResult.Items[0].Score.Should().Be(2); + searchResult.Items[1].Item.Username.Should().Be("bababa"); + searchResult.Items[1].Score.Should().Be(1); + } + } +} diff --git a/BackEnd/Timeline.Tests/Services/ServiceTestBase.cs b/BackEnd/Timeline.Tests/Services/ServiceTestBase.cs new file mode 100644 index 00000000..5a3e1e19 --- /dev/null +++ b/BackEnd/Timeline.Tests/Services/ServiceTestBase.cs @@ -0,0 +1,71 @@ +using Microsoft.Extensions.Logging.Abstractions; +using System.Threading.Tasks; +using Timeline.Entities; +using Timeline.Services; +using Timeline.Tests.Helpers; +using Xunit; +using Xunit.Abstractions; + +namespace Timeline.Tests.Services +{ + public abstract class ServiceTestBase : IAsyncLifetime + { + protected TestDatabase TestDatabase { get; } + protected DatabaseContext Database { get; private set; } = default!; + + private readonly ITestOutputHelper? _testOutputHelper; + + protected TestClock Clock { get; } = new TestClock(); + protected UserService UserService { get; private set; } = default!; + protected TimelineService TimelineService { get; private set; } = default!; + + protected long UserId { get; private set; } + protected long AdminId { get; private set; } + + protected ServiceTestBase(bool databaseCreateUsers = true, ITestOutputHelper? testOutputHelper = null) + { + _testOutputHelper = testOutputHelper; + TestDatabase = new TestDatabase(databaseCreateUsers); + } + + protected ServiceTestBase(ITestOutputHelper? testOutputHelper) : this(true, testOutputHelper) { } + + public async Task InitializeAsync() + { + await TestDatabase.InitializeAsync(); + Database = TestDatabase.CreateContext(_testOutputHelper); + + UserService = new UserService(NullLogger.Instance, Database, new PasswordService(), Clock); + TimelineService = new TimelineService(Database, UserService, Clock); + + UserId = await UserService.GetUserIdByUsername("user"); + AdminId = await UserService.GetUserIdByUsername("admin"); + + await OnInitializeAsync(); + OnInitialize(); + } + + public async Task DisposeAsync() + { + OnDispose(); + await OnDisposeAsync(); + await Database.DisposeAsync(); + await TestDatabase.DisposeAsync(); + } + + + protected virtual void OnInitialize() { } + protected virtual void OnDispose() { } + + + protected virtual Task OnInitializeAsync() + { + return Task.CompletedTask; + } + + protected virtual Task OnDisposeAsync() + { + return Task.CompletedTask; + } + } +} diff --git a/BackEnd/Timeline.Tests/Services/UserDeleteServiceTest.cs b/BackEnd/Timeline.Tests/Services/UserDeleteServiceTest.cs index 59c0a9af..10014d2b 100644 --- a/BackEnd/Timeline.Tests/Services/UserDeleteServiceTest.cs +++ b/BackEnd/Timeline.Tests/Services/UserDeleteServiceTest.cs @@ -8,12 +8,12 @@ using Xunit; namespace Timeline.Tests.Services { - public class UserDeleteServiceTest : DatabaseBasedTest + public class UserDeleteServiceTest : ServiceTestBase { private readonly Mock _mockTimelinePostService = new Mock(); private UserDeleteService _service = default!; - protected override void OnDatabaseCreated() + protected override void OnInitialize() { _service = new UserDeleteService(NullLogger.Instance, Database, _mockTimelinePostService.Object); } diff --git a/BackEnd/Timeline.Tests/Services/UserPermissionServiceTest.cs b/BackEnd/Timeline.Tests/Services/UserPermissionServiceTest.cs index f20a7d62..0c43c025 100644 --- a/BackEnd/Timeline.Tests/Services/UserPermissionServiceTest.cs +++ b/BackEnd/Timeline.Tests/Services/UserPermissionServiceTest.cs @@ -7,7 +7,7 @@ using Xunit; namespace Timeline.Tests.Services { - public class UserPermissionServiceTest : DatabaseBasedTest + public class UserPermissionServiceTest : ServiceTestBase { private UserPermissionService _service = default!; @@ -16,7 +16,7 @@ namespace Timeline.Tests.Services } - protected override void OnDatabaseCreated() + protected override void OnInitialize() { _service = new UserPermissionService(Database); } diff --git a/BackEnd/Timeline/Services/SearchService.cs b/BackEnd/Timeline/Services/SearchService.cs index eac81fd0..680ef9e3 100644 --- a/BackEnd/Timeline/Services/SearchService.cs +++ b/BackEnd/Timeline/Services/SearchService.cs @@ -39,6 +39,9 @@ namespace Timeline.Services /// Search results. /// Thrown when is null. /// Thrown when is empty. + /// + /// Implementation should promise high score is at first. + /// Task> SearchTimeline(string query); /// @@ -48,6 +51,9 @@ namespace Timeline.Services /// Search results. /// Thrown when is null. /// Thrown when is empty. + /// + /// Implementation should promise high score is at first. + /// Task> SearchUser(string query); } -- cgit v1.2.3 From 667f14149f32756a80018ead27102c63270644f4 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 17 Jan 2021 22:56:05 +0800 Subject: feat: Add search controller. --- BackEnd/Timeline/Controllers/SearchController.cs | 47 ++++++++++++++++++++++++ BackEnd/Timeline/Startup.cs | 2 + 2 files changed, 49 insertions(+) create mode 100644 BackEnd/Timeline/Controllers/SearchController.cs (limited to 'BackEnd/Timeline') diff --git a/BackEnd/Timeline/Controllers/SearchController.cs b/BackEnd/Timeline/Controllers/SearchController.cs new file mode 100644 index 00000000..915938de --- /dev/null +++ b/BackEnd/Timeline/Controllers/SearchController.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Timeline.Models.Http; +using Timeline.Models.Mapper; +using Timeline.Services; + +namespace Timeline.Controllers +{ + /// + /// Api related to search timelines or users. + /// + [ApiController] + [ProducesErrorResponseType(typeof(CommonResponse))] + [Route("search")] + public class SearchController : Controller + { + private readonly ISearchService _service; + private readonly TimelineMapper _timelineMapper; + private readonly UserMapper _userMapper; + + public SearchController(ISearchService service, TimelineMapper timelineMapper, UserMapper userMapper) + { + _service = service; + _timelineMapper = timelineMapper; + _userMapper = userMapper; + } + + [HttpGet("timelines")] + public async Task>> TimelineSearch([FromQuery(Name = "q"), Required(AllowEmptyStrings = false)] string query) + { + var searchResult = await _service.SearchTimeline(query); + var timelines = searchResult.Items.Select(i => i.Item).ToList(); + return await _timelineMapper.MapToHttp(timelines, Url, this.GetOptionalUserId()); + } + + [HttpGet("users")] + public async Task>> UserSearch([FromQuery(Name = "q"), Required(AllowEmptyStrings = false)] string query) + { + var searchResult = await _service.SearchUser(query); + var users = searchResult.Items.Select(i => i.Item).ToList(); + return await _userMapper.MapToHttp(users, Url); + } + } +} diff --git a/BackEnd/Timeline/Startup.cs b/BackEnd/Timeline/Startup.cs index cb99c138..0fab798b 100644 --- a/BackEnd/Timeline/Startup.cs +++ b/BackEnd/Timeline/Startup.cs @@ -120,6 +120,8 @@ namespace Timeline services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddOpenApiDocs(); if (_frontEndMode == FrontEndMode.Mock) -- cgit v1.2.3 From 9494c9717f0d9983ec1c8db092387fef7199b00d Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 19 Jan 2021 15:24:39 +0800 Subject: test: Add integrated tests for search api. --- .../IntegratedTests/BaseTimelineTest.cs | 2 +- .../IntegratedTests/BookmarkTimelineTest.cs | 4 +- .../IntegratedTests/HighlightTimelineTest.cs | 4 +- .../IntegratedTests/HttpClientTestExtensions.cs | 5 ++ .../Timeline.Tests/IntegratedTests/SearchTest.cs | 63 ++++++++++++++++++++++ .../Timeline.Tests/IntegratedTests/TimelineTest.cs | 8 +-- BackEnd/Timeline/Controllers/SearchController.cs | 14 +++++ BackEnd/Timeline/Controllers/TimelineController.cs | 2 +- BackEnd/Timeline/Models/Http/TimelineController.cs | 2 +- 9 files changed, 93 insertions(+), 11 deletions(-) create mode 100644 BackEnd/Timeline.Tests/IntegratedTests/SearchTest.cs (limited to 'BackEnd/Timeline') diff --git a/BackEnd/Timeline.Tests/IntegratedTests/BaseTimelineTest.cs b/BackEnd/Timeline.Tests/IntegratedTests/BaseTimelineTest.cs index 0bf3b2b2..006b5128 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests/BaseTimelineTest.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests/BaseTimelineTest.cs @@ -15,7 +15,7 @@ namespace Timeline.Tests.IntegratedTests for (int i = 0; i <= 3; i++) { using var client = await CreateClientAs(i); - await client.TestPostAsync("timelines", new TimelineCreateRequest { Name = $"t{i}" }); + await client.TestPostAsync("timelines", new HttpTimelineCreateRequest { Name = $"t{i}" }); } } diff --git a/BackEnd/Timeline.Tests/IntegratedTests/BookmarkTimelineTest.cs b/BackEnd/Timeline.Tests/IntegratedTests/BookmarkTimelineTest.cs index 99cf6d3a..23d67d44 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests/BookmarkTimelineTest.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests/BookmarkTimelineTest.cs @@ -34,7 +34,7 @@ namespace Timeline.Tests.IntegratedTests public async Task ShouldWork() { using var client = await CreateClientAsUser(); - await client.TestPostAsync("timelines", new TimelineCreateRequest { Name = "t1" }); + await client.TestPostAsync("timelines", new HttpTimelineCreateRequest { Name = "t1" }); { @@ -88,7 +88,7 @@ namespace Timeline.Tests.IntegratedTests public async Task TimelineGet_IsBookmarkField_ShouldWork() { using var client = await CreateClientAsUser(); - await client.TestPostAsync("timelines", new TimelineCreateRequest { Name = "t" }); + await client.TestPostAsync("timelines", new HttpTimelineCreateRequest { Name = "t" }); { var t = await client.TestGetAsync("timelines/t"); diff --git a/BackEnd/Timeline.Tests/IntegratedTests/HighlightTimelineTest.cs b/BackEnd/Timeline.Tests/IntegratedTests/HighlightTimelineTest.cs index 440759f4..cfc41468 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests/HighlightTimelineTest.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests/HighlightTimelineTest.cs @@ -35,7 +35,7 @@ namespace Timeline.Tests.IntegratedTests { { using var client1 = await CreateClientAsUser(); - await client1.TestPostAsync("timelines", new TimelineCreateRequest { Name = "t1" }); + await client1.TestPostAsync("timelines", new HttpTimelineCreateRequest { Name = "t1" }); } using var client = await CreateClientAsAdministrator(); @@ -91,7 +91,7 @@ namespace Timeline.Tests.IntegratedTests public async Task TimelineGet_IsHighlighField_Should_Work() { using var client = await CreateClientAsAdministrator(); - await client.TestPostAsync("timelines", new TimelineCreateRequest { Name = "t" }); + await client.TestPostAsync("timelines", new HttpTimelineCreateRequest { Name = "t" }); { var t = await client.TestGetAsync("timelines/t"); diff --git a/BackEnd/Timeline.Tests/IntegratedTests/HttpClientTestExtensions.cs b/BackEnd/Timeline.Tests/IntegratedTests/HttpClientTestExtensions.cs index b219f092..9848564f 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests/HttpClientTestExtensions.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests/HttpClientTestExtensions.cs @@ -78,6 +78,11 @@ namespace Timeline.Tests.IntegratedTests return await client.TestJsonSendAsync(HttpMethod.Put, url, jsonBody, expectedStatusCode: expectedStatusCode); } + public static async Task TestPatchAsync(this HttpClient client, string url, object? jsonBody = null, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) + { + await client.TestJsonSendAsync(HttpMethod.Patch, url, jsonBody, expectedStatusCode: expectedStatusCode); + } + public static async Task TestPatchAsync(this HttpClient client, string url, object? jsonBody = null, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) { return await client.TestJsonSendAsync(HttpMethod.Patch, url, jsonBody, expectedStatusCode: expectedStatusCode); diff --git a/BackEnd/Timeline.Tests/IntegratedTests/SearchTest.cs b/BackEnd/Timeline.Tests/IntegratedTests/SearchTest.cs new file mode 100644 index 00000000..f96acfea --- /dev/null +++ b/BackEnd/Timeline.Tests/IntegratedTests/SearchTest.cs @@ -0,0 +1,63 @@ +using FluentAssertions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Timeline.Models.Http; +using Xunit; + +namespace Timeline.Tests.IntegratedTests +{ + public class SearchTest : IntegratedTestBase + { + [Fact] + public async Task TimelineSearch_Should_Work() + { + var client = await CreateClientAsUser(); + + { + await client.TestPostAsync("timelines", new HttpTimelineCreateRequest { Name = "hahaha" }); + await client.TestPostAsync("timelines", new HttpTimelineCreateRequest { Name = "bababa" }); + await client.TestPatchAsync("timelines/bababa", new HttpTimelinePatchRequest { Title = "hahaha" }); + await client.TestPostAsync("timelines", new HttpTimelineCreateRequest { Name = "gagaga" }); + } + + { + var res = await client.TestGetAsync>("search/timelines?q=hah"); + res.Should().HaveCount(2); + res[0].Name.Should().Be("hahaha"); + res[1].Name.Should().Be("bababa"); + } + + { + var res = await client.TestGetAsync>("search/timelines?q=wuhu"); + res.Should().BeEmpty(); + } + } + + [Fact] + public async Task UserSearch_Should_Work() + { + var client = await CreateClientAsAdministrator(); + + { + await client.TestPostAsync("userop/createuser", new HttpCreateUserRequest { Username = "hahaha", Password = "p" }); + await client.TestPostAsync("userop/createuser", new HttpCreateUserRequest { Username = "bababa", Password = "p" }); + await client.TestPatchAsync("users/bababa", new HttpUserPatchRequest { Nickname = "hahaha" }); + await client.TestPostAsync("userop/createuser", new HttpCreateUserRequest { Username = "gagaga", Password = "p" }); + } + + { + var res = await client.TestGetAsync>("search/users?q=hah"); + res.Should().HaveCount(2); + res[0].Username.Should().Be("hahaha"); + res[1].Username.Should().Be("bababa"); + } + + { + var res = await client.TestGetAsync>("search/users?q=wuhu"); + res.Should().BeEmpty(); + } + } + } +} diff --git a/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs b/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs index 66261b36..4247e572 100644 --- a/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs +++ b/BackEnd/Timeline.Tests/IntegratedTests/TimelineTest.cs @@ -179,20 +179,20 @@ namespace Timeline.Tests.IntegratedTests { { using var client = await CreateDefaultClient(); - await client.TestPostAssertUnauthorizedAsync("timelines", new TimelineCreateRequest { Name = "aaa" }); + await client.TestPostAssertUnauthorizedAsync("timelines", new HttpTimelineCreateRequest { Name = "aaa" }); } { using var client = await CreateClientAsUser(); - await client.TestPostAssertInvalidModelAsync("timelines", new TimelineCreateRequest { Name = "!!!" }); + await client.TestPostAssertInvalidModelAsync("timelines", new HttpTimelineCreateRequest { Name = "!!!" }); { - var body = await client.TestPostAsync("timelines", new TimelineCreateRequest { Name = "aaa" }); + var body = await client.TestPostAsync("timelines", new HttpTimelineCreateRequest { Name = "aaa" }); body.Should().BeEquivalentTo(await client.GetTimelineAsync("aaa")); } - await client.TestPostAssertErrorAsync("timelines", new TimelineCreateRequest { Name = "aaa" }, errorCode: ErrorCodes.TimelineController.NameConflict); + await client.TestPostAssertErrorAsync("timelines", new HttpTimelineCreateRequest { Name = "aaa" }, errorCode: ErrorCodes.TimelineController.NameConflict); } } diff --git a/BackEnd/Timeline/Controllers/SearchController.cs b/BackEnd/Timeline/Controllers/SearchController.cs index 915938de..dec876b6 100644 --- a/BackEnd/Timeline/Controllers/SearchController.cs +++ b/BackEnd/Timeline/Controllers/SearchController.cs @@ -28,7 +28,14 @@ namespace Timeline.Controllers _userMapper = userMapper; } + /// + /// Search timelines whose name or title contains query string case-insensitively. + /// + /// The string to contain. + /// Timelines with most related at first. [HttpGet("timelines")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] public async Task>> TimelineSearch([FromQuery(Name = "q"), Required(AllowEmptyStrings = false)] string query) { var searchResult = await _service.SearchTimeline(query); @@ -36,7 +43,14 @@ namespace Timeline.Controllers return await _timelineMapper.MapToHttp(timelines, Url, this.GetOptionalUserId()); } + /// + /// Search users whose username or nick contains query string case-insensitively. + /// + /// The string to contain. + /// Users with most related at first. [HttpGet("users")] + [ProducesResponseType(200)] + [ProducesResponseType(400)] public async Task>> UserSearch([FromQuery(Name = "q"), Required(AllowEmptyStrings = false)] string query) { var searchResult = await _service.SearchUser(query); diff --git a/BackEnd/Timeline/Controllers/TimelineController.cs b/BackEnd/Timeline/Controllers/TimelineController.cs index b2e37b15..5d484388 100644 --- a/BackEnd/Timeline/Controllers/TimelineController.cs +++ b/BackEnd/Timeline/Controllers/TimelineController.cs @@ -441,7 +441,7 @@ namespace Timeline.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task> TimelineCreate([FromBody] TimelineCreateRequest body) + public async Task> TimelineCreate([FromBody] HttpTimelineCreateRequest body) { var userId = this.GetUserId(); diff --git a/BackEnd/Timeline/Models/Http/TimelineController.cs b/BackEnd/Timeline/Models/Http/TimelineController.cs index f6039b35..257076f0 100644 --- a/BackEnd/Timeline/Models/Http/TimelineController.cs +++ b/BackEnd/Timeline/Models/Http/TimelineController.cs @@ -43,7 +43,7 @@ namespace Timeline.Models.Http /// /// Create timeline request model. /// - public class TimelineCreateRequest + public class HttpTimelineCreateRequest { /// /// Name of the new timeline. Must be a valid name. -- cgit v1.2.3