diff --git a/ShoppingAssistantApi.Api/Mutations/WishlistsMutation.cs b/ShoppingAssistantApi.Api/Mutations/WishlistsMutation.cs index 3bd1d7c..c4dde4c 100644 --- a/ShoppingAssistantApi.Api/Mutations/WishlistsMutation.cs +++ b/ShoppingAssistantApi.Api/Mutations/WishlistsMutation.cs @@ -7,11 +7,11 @@ namespace ShoppingAssistantApi.Api.Mutations; [ExtendObjectType(OperationTypeNames.Mutation)] public class WishlistsMutation { - public Task StartPersonalWishlist(WishlistCreateDto dto, CancellationToken cancellationToken, + public Task StartPersonalWishlistAsync(WishlistCreateDto dto, CancellationToken cancellationToken, [Service] IWishlistsService wishlistsService) => wishlistsService.StartPersonalWishlistAsync(dto, cancellationToken); - public Task AddMessageToPersonalWishlist(string wishlistId, MessageCreateDto dto, CancellationToken cancellationToken, + public Task AddMessageToPersonalWishlistAsync(string wishlistId, MessageCreateDto dto, CancellationToken cancellationToken, [Service] IWishlistsService wishlistsService) => wishlistsService.AddMessageToPersonalWishlistAsync(wishlistId, dto, cancellationToken); diff --git a/ShoppingAssistantApi.Api/Queries/WishlistsQuery.cs b/ShoppingAssistantApi.Api/Queries/WishlistsQuery.cs index 389864f..49a229f 100644 --- a/ShoppingAssistantApi.Api/Queries/WishlistsQuery.cs +++ b/ShoppingAssistantApi.Api/Queries/WishlistsQuery.cs @@ -9,12 +9,17 @@ namespace ShoppingAssistantApi.Api.Queries; public class WishlistsQuery { [Authorize] - public Task> GetPersonalWishlistsPageAsync(int pageNumber, int pageSize, CancellationToken cancellationToken, - [Service] IWishlistsService wishlistsService) + public Task> GetPersonalWishlistsPageAsync(int pageNumber, int pageSize, + CancellationToken cancellationToken, [Service] IWishlistsService wishlistsService) => wishlistsService.GetPersonalWishlistsPageAsync(pageNumber, pageSize, cancellationToken); [Authorize] public Task GetPersonalWishlistAsync(string wishlistId, CancellationToken cancellationToken, - [Service] IWishlistsService wishlistsService) + [Service] IWishlistsService wishlistsService) => wishlistsService.GetPersonalWishlistAsync(wishlistId, cancellationToken); + + [Authorize] + public Task> GetMessagesPageFromPersonalWishlistAsync(string wishlistId, int pageNumber, int pageSize, + CancellationToken cancellationToken, [Service] IWishlistsService wishlistsService) + => wishlistsService.GetMessagesPageFromPersonalWishlistAsync(wishlistId, pageNumber, pageSize, cancellationToken); } diff --git a/ShoppingAssistantApi.Application/IRepositories/IMessagerepository.cs b/ShoppingAssistantApi.Application/IRepositories/IMessagerepository.cs index 5387549..3d0483e 100644 --- a/ShoppingAssistantApi.Application/IRepositories/IMessagerepository.cs +++ b/ShoppingAssistantApi.Application/IRepositories/IMessagerepository.cs @@ -1,5 +1,9 @@ +using System.Linq.Expressions; using ShoppingAssistantApi.Domain.Entities; namespace ShoppingAssistantApi.Application.IRepositories; -public interface IMessagesRepository : IBaseRepository { } +public interface IMessagesRepository : IBaseRepository +{ + Task> GetPageStartingFromEndAsync(int pageNumber, int pageSize, Expression> predicate, CancellationToken cancellationToken); +} diff --git a/ShoppingAssistantApi.Application/IServices/IWishlistService.cs b/ShoppingAssistantApi.Application/IServices/IWishlistService.cs index 41081c6..c2cd5f3 100644 --- a/ShoppingAssistantApi.Application/IServices/IWishlistService.cs +++ b/ShoppingAssistantApi.Application/IServices/IWishlistService.cs @@ -14,5 +14,7 @@ public interface IWishlistsService Task GetPersonalWishlistAsync(string wishlistId, CancellationToken cancellationToken); + Task> GetMessagesPageFromPersonalWishlistAsync(string wishlistId, int pageNumber, int pageSize, CancellationToken cancellationToken); + Task DeletePersonalWishlistAsync(string wishlistId, CancellationToken cancellationToken); } diff --git a/ShoppingAssistantApi.Infrastructure/Services/WishlistsService.cs b/ShoppingAssistantApi.Infrastructure/Services/WishlistsService.cs index a5166f2..6ec7a20 100644 --- a/ShoppingAssistantApi.Infrastructure/Services/WishlistsService.cs +++ b/ShoppingAssistantApi.Infrastructure/Services/WishlistsService.cs @@ -47,6 +47,8 @@ public class WishlistsService : IWishlistsService { Text = dto.FirstMessageText, Role = MessageRoles.User.ToString(), + CreatedById = (ObjectId) GlobalUser.Id, + CreatedDateUtc = DateTime.UtcNow, WishlistId = createdWishlist.Id }; var createdMessage = await _messagesRepository.AddAsync(newMessage, cancellationToken); @@ -62,10 +64,11 @@ public class WishlistsService : IWishlistsService { throw new InvalidDataException("Provided id is invalid."); } - newMessage.WishlistId = wishlistObjectId; + newMessage.Role = MessageRoles.User.ToString(); newMessage.CreatedById = (ObjectId) GlobalUser.Id; newMessage.CreatedDateUtc = DateTime.UtcNow; + newMessage.WishlistId = wishlistObjectId; await TryGetPersonalWishlist(wishlistObjectId, cancellationToken); @@ -94,6 +97,22 @@ public class WishlistsService : IWishlistsService return _mapper.Map(entity); } + public async Task> GetMessagesPageFromPersonalWishlistAsync(string wishlistId, int pageNumber, int pageSize, CancellationToken cancellationToken) + { + if (!ObjectId.TryParse(wishlistId, out var wishlistObjectId)) + { + throw new InvalidDataException("Provided id is invalid."); + } + + await TryGetPersonalWishlist(wishlistObjectId, cancellationToken); + + var entities = await _messagesRepository.GetPageStartingFromEndAsync(pageNumber, pageSize, x => x.WishlistId == wishlistObjectId, cancellationToken); + + var dtos = _mapper.Map>(entities); + var count = await _messagesRepository.GetCountAsync(x => x.WishlistId == wishlistObjectId, cancellationToken); + return new PagedList(dtos, pageNumber, pageSize, count); + } + public async Task DeletePersonalWishlistAsync(string wishlistId, CancellationToken cancellationToken) { if (!ObjectId.TryParse(wishlistId, out var wishlistObjectId)) diff --git a/ShoppingAssistantApi.Persistance/PersistanceExtentions/DbInitialaizer.cs b/ShoppingAssistantApi.Persistance/PersistanceExtentions/DbInitialaizer.cs index a63c8f7..2d3af82 100644 --- a/ShoppingAssistantApi.Persistance/PersistanceExtentions/DbInitialaizer.cs +++ b/ShoppingAssistantApi.Persistance/PersistanceExtentions/DbInitialaizer.cs @@ -27,6 +27,8 @@ public class DbInitialaizer private readonly IMongoCollection _wishlistCollection; + private readonly IMongoCollection _messageCollection; + public IEnumerable Roles { get; set; } public DbInitialaizer(IServiceProvider serviceProvider) @@ -35,8 +37,9 @@ public class DbInitialaizer _rolesService = serviceProvider.GetService(); _userManager = serviceProvider.GetService(); _tokensService = serviceProvider.GetService(); - _wishlistCollection = serviceProvider.GetService().Db.GetCollection("Wishlists"); _userCollection = serviceProvider.GetService().Db.GetCollection("Users"); + _wishlistCollection = serviceProvider.GetService().Db.GetCollection("Wishlists"); + _messageCollection = serviceProvider.GetService().Db.GetCollection("Messages"); } public async Task InitialaizeDb(CancellationToken cancellationToken) @@ -172,45 +175,79 @@ public class DbInitialaizer var user1 = await (await _userCollection.FindAsync(x => x.Email.Equals("shopping.assistant.team@gmail.com"))).FirstAsync(); var user2 = await (await _userCollection.FindAsync(x => x.Email.Equals("mykhailo.bilodid@nure.ua"))).FirstAsync(); + var wishlistId1 = ObjectId.Parse("ab79cde6f69abcd3efab65cd"); + var wishlistId2 = ObjectId.Parse("ab6c2c2d9edf39abcd1ef9ab"); + var wishlists = new Wishlist[] { new Wishlist { - Id = ObjectId.Parse("ab79cde6f69abcd3efab65cd"), + Id = wishlistId1, Name = "Gaming PC", Type = WishlistTypes.Product.ToString(), CreatedById = user1.Id, - Messages = new Message[] - { - new Message - { - Text = "Prompt", - Role = MessageRoles.User.ToString(), - }, - new Message - { - Text = "Answer", - Role = MessageRoles.Application.ToString(), - }, - } }, new Wishlist { - Id = ObjectId.Parse("ab6c2c2d9edf39abcd1ef9ab"), + Id = wishlistId2, Name = "Generic Wishlist Name", Type = WishlistTypes.Product.ToString(), CreatedById = user2.Id, - Messages = new Message[] - { - new Message - { - Text = "Prompt", - Role = MessageRoles.User.ToString(), - } - } } }; await _wishlistCollection.InsertManyAsync(wishlists); + + var messages = new Message[] + { + new Message + { + Text = "Message 1", + Role = MessageRoles.User.ToString(), + WishlistId = wishlistId1, + CreatedById = user1.Id, + }, + new Message + { + Text = "Message 2", + Role = MessageRoles.Application.ToString(), + WishlistId = wishlistId1, + }, + new Message + { + Text = "Message 3", + Role = MessageRoles.User.ToString(), + WishlistId = wishlistId1, + CreatedById = user1.Id, + }, + new Message + { + Text = "Message 4", + Role = MessageRoles.Application.ToString(), + WishlistId = wishlistId1, + }, + new Message + { + Text = "Message 5", + Role = MessageRoles.User.ToString(), + WishlistId = wishlistId1, + CreatedById = user1.Id, + }, + new Message + { + Text = "Message 6", + Role = MessageRoles.Application.ToString(), + WishlistId = wishlistId1, + }, + new Message + { + Text = "Prompt", + Role = MessageRoles.User.ToString(), + WishlistId = wishlistId2, + CreatedById = user2.Id, + } + }; + + await _messageCollection.InsertManyAsync(messages); } } diff --git a/ShoppingAssistantApi.Persistance/Repositories/MessagesRepository.cs b/ShoppingAssistantApi.Persistance/Repositories/MessagesRepository.cs index 06481f6..23f9b8f 100644 --- a/ShoppingAssistantApi.Persistance/Repositories/MessagesRepository.cs +++ b/ShoppingAssistantApi.Persistance/Repositories/MessagesRepository.cs @@ -1,3 +1,5 @@ +using System.Linq.Expressions; +using MongoDB.Driver; using ShoppingAssistantApi.Application.IRepositories; using ShoppingAssistantApi.Domain.Entities; using ShoppingAssistantApi.Persistance.Database; @@ -7,4 +9,14 @@ namespace ShoppingAssistantApi.Persistance.Repositories; public class MessagesRepository : BaseRepository, IMessagesRepository { public MessagesRepository(MongoDbContext db) : base(db, "Messages") { } + + public async Task> GetPageStartingFromEndAsync(int pageNumber, int pageSize, Expression> predicate, CancellationToken cancellationToken) + { + var messageCount = await GetCountAsync(predicate, cancellationToken); + + return await _collection.Find(predicate) + .Skip((messageCount / pageSize - pageNumber) * pageSize) + .Limit(pageSize) + .ToListAsync(cancellationToken); + } } diff --git a/ShoppingAssistantApi.Tests/Tests/WishlistsTests.cs b/ShoppingAssistantApi.Tests/Tests/WishlistsTests.cs index 93f37f8..2c64d42 100644 --- a/ShoppingAssistantApi.Tests/Tests/WishlistsTests.cs +++ b/ShoppingAssistantApi.Tests/Tests/WishlistsTests.cs @@ -185,6 +185,44 @@ public class WishlistsTests : IClassFixture> Assert.Equal(user.Id, messageCreatedById); } + [Fact] + public async Task GetMessagesPageFromPersonalWishlist_ValidPageNumberAndSizeValidWishlistIdOrAuthorizedAccess_ReturnsWishlistModel() + { + var tokensModel = await AccessExtention.Login(WISHLIST_TESTING_USER_EMAIL, WISHLIST_TESTING_USER_PASSWORD, _httpClient); + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken); + var user = await UserExtention.GetCurrentUser(_httpClient); + + var mutation = new + { + query = "query messagesPageFromPersonalWishlist($wishlistId: String!, $pageNumber: Int!, $pageSize: Int!) { messagesPageFromPersonalWishlist (wishlistId: $wishlistId, pageNumber: $pageNumber, pageSize: $pageSize) { hasNextPage, hasPreviousPage, items { id, text, role, createdById }, pageNumber, pageSize, totalItems, totalPages } }", + variables = new + { + wishlistId = WISHLIST_TESTING_VALID_WISHLIST_ID, + pageNumber = 1, + pageSize = 2 + } + }; + + var jsonPayload = JsonConvert.SerializeObject(mutation); + var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"); + + using var response = await _httpClient.PostAsync("graphql", content); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var responseString = await response.Content.ReadAsStringAsync(); + var document = JsonConvert.DeserializeObject(responseString); + + Console.WriteLine(document.data.messagesPageFromPersonalWishlist); + + var messagesPageFromPersonalWishlist = Enumerable.ToList(document.data.messagesPageFromPersonalWishlist.items); + var firstMessageInPage = messagesPageFromPersonalWishlist[0]; + var secondMessageInPage = messagesPageFromPersonalWishlist[1]; + + Assert.Equal("Message 5", (string) firstMessageInPage.text); + Assert.Equal(MessageRoles.User.ToString(), (string) firstMessageInPage.role); + Assert.Equal(user.Id, (string) firstMessageInPage.createdById); + } + [Fact] public async Task DeletePersonalWishlist_ValidWishlistIdOrAuthorizedAccess_ReturnsWishlistModel() { @@ -323,6 +361,106 @@ public class WishlistsTests : IClassFixture> Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); } + [Fact] + public async Task GetMessagesPageFromPersonalWishlist_InValidPageNumber_ReturnsInternalServerError() + { + var tokensModel = await AccessExtention.Login(WISHLIST_TESTING_USER_EMAIL, WISHLIST_TESTING_USER_PASSWORD, _httpClient); + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken); + var user = await UserExtention.GetCurrentUser(_httpClient); + + var mutation = new + { + query = "query messagesPageFromPersonalWishlist($wishlistId: String!, $pageNumber: Int!, $pageSize: Int!) { messagesPageFromPersonalWishlist (wishlistId: $wishlistId, pageNumber: $pageNumber, pageSize: $pageSize) { hasNextPage, hasPreviousPage, items { id, text, role, createdById }, pageNumber, pageSize, totalItems, totalPages } }", + variables = new + { + wishlistId = WISHLIST_TESTING_VALID_WISHLIST_ID, + pageNumber = 4, + pageSize = 2 + } + }; + + var jsonPayload = JsonConvert.SerializeObject(mutation); + var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"); + + using var response = await _httpClient.PostAsync("graphql", content); + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + } + + [Fact] + public async Task GetMessagesPageFromPersonalWishlist_InValidPageSize_ReturnsInternalServerError() + { + var tokensModel = await AccessExtention.Login(WISHLIST_TESTING_USER_EMAIL, WISHLIST_TESTING_USER_PASSWORD, _httpClient); + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken); + var user = await UserExtention.GetCurrentUser(_httpClient); + + var mutation = new + { + query = "query messagesPageFromPersonalWishlist($wishlistId: String!, $pageNumber: Int!, $pageSize: Int!) { messagesPageFromPersonalWishlist (wishlistId: $wishlistId, pageNumber: $pageNumber, pageSize: $pageSize) { hasNextPage, hasPreviousPage, items { id, text, role, createdById }, pageNumber, pageSize, totalItems, totalPages } }", + variables = new + { + wishlistId = WISHLIST_TESTING_VALID_WISHLIST_ID, + pageNumber = 1, + pageSize = 10 + } + }; + + var jsonPayload = JsonConvert.SerializeObject(mutation); + var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"); + + using var response = await _httpClient.PostAsync("graphql", content); + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + } + + [Fact] + public async Task GetMessagesPageFromPersonalWishlist_InValidWishlistId_ReturnsInternalServerError() + { + var tokensModel = await AccessExtention.Login(WISHLIST_TESTING_USER_EMAIL, WISHLIST_TESTING_USER_PASSWORD, _httpClient); + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken); + var user = await UserExtention.GetCurrentUser(_httpClient); + + var mutation = new + { + query = "query messagesPageFromPersonalWishlist($wishlistId: String!, $pageNumber: Int!, $pageSize: Int!) { messagesPageFromPersonalWishlist (wishlistId: $wishlistId, pageNumber: $pageNumber, pageSize: $pageSize) { hasNextPage, hasPreviousPage, items { id, text, role, createdById }, pageNumber, pageSize, totalItems, totalPages } }", + variables = new + { + wishlistId = WISHLIST_TESTING_INVALID_WISHLIST_ID, + pageNumber = 1, + pageSize = 2 + } + }; + + var jsonPayload = JsonConvert.SerializeObject(mutation); + var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"); + + using var response = await _httpClient.PostAsync("graphql", content); + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + } + + [Fact] + public async Task GetMessagesPageFromPersonalWishlist_UnAuthorizedAccess_ReturnsInternalServerError() + { + var tokensModel = await AccessExtention.Login(WISHLIST_TESTING_USER_EMAIL, WISHLIST_TESTING_USER_PASSWORD, _httpClient); + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken); + var user = await UserExtention.GetCurrentUser(_httpClient); + + var mutation = new + { + query = "query messagesPageFromPersonalWishlist($wishlistId: String!, $pageNumber: Int!, $pageSize: Int!) { messagesPageFromPersonalWishlist (wishlistId: $wishlistId, pageNumber: $pageNumber, pageSize: $pageSize) { hasNextPage, hasPreviousPage, items { id, text, role, createdById }, pageNumber, pageSize, totalItems, totalPages } }", + variables = new + { + wishlistId = WISHLIST_TESTING_OTHER_USER_WISHLIST_ID, + pageNumber = 1, + pageSize = 2 + } + }; + + var jsonPayload = JsonConvert.SerializeObject(mutation); + var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"); + + using var response = await _httpClient.PostAsync("graphql", content); + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + } + [Fact] public async Task DeletePersonalWishlist_InValidWishlistId_ReturnsInternalServerError() { @@ -345,4 +483,27 @@ public class WishlistsTests : IClassFixture> using var response = await _httpClient.PostAsync("graphql", content); Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); } + + [Fact] + public async Task DeletePersonalWishlist_UnAuthorizedAccess_ReturnsInternalServerError() + { + var tokensModel = await AccessExtention.Login(WISHLIST_TESTING_USER_EMAIL, WISHLIST_TESTING_USER_PASSWORD, _httpClient); + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokensModel.AccessToken); + var user = await UserExtention.GetCurrentUser(_httpClient); + + var mutation = new + { + query = "mutation deletePersonalWishlist($wishlistId: String!) { deletePersonalWishlist (wishlistId: $wishlistId) { createdById, id, name, type } }", + variables = new + { + wishlistId = WISHLIST_TESTING_OTHER_USER_WISHLIST_ID + } + }; + + var jsonPayload = JsonConvert.SerializeObject(mutation); + var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json"); + + using var response = await _httpClient.PostAsync("graphql", content); + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + } }