diff --git a/OutOfSchool/OutOfSchool.DataAccess/Repository/ChatMessageRepository.cs b/OutOfSchool/OutOfSchool.DataAccess/Repository/ChatMessageRepository.cs new file mode 100644 index 0000000000..685c2c5baf --- /dev/null +++ b/OutOfSchool/OutOfSchool.DataAccess/Repository/ChatMessageRepository.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using OutOfSchool.Services.Models; +using OutOfSchool.Services.Models.ChatWorkshop; + +namespace OutOfSchool.Services.Repository; + +public class ChatMessageRepository : SensitiveEntityRepository, IChatMessageRepository +{ + public ChatMessageRepository(OutOfSchoolDbContext dbContext) + : base(dbContext) + { + } + + public Task CountUnreadMessagesAsync(Guid workshopId) + { + return dbContext.ChatMessageWorkshops + .Include(chr => chr.ChatRoom) + .Where(x => x.ChatRoom.WorkshopId == workshopId && x.ReadDateTime == null && !x.SenderRoleIsProvider) + .CountAsync(); + } +} \ No newline at end of file diff --git a/OutOfSchool/OutOfSchool.DataAccess/Repository/IChatMessageRepository.cs b/OutOfSchool/OutOfSchool.DataAccess/Repository/IChatMessageRepository.cs new file mode 100644 index 0000000000..2ff38f0d42 --- /dev/null +++ b/OutOfSchool/OutOfSchool.DataAccess/Repository/IChatMessageRepository.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OutOfSchool.Services.Models.ChatWorkshop; + +namespace OutOfSchool.Services.Repository; + +public interface IChatMessageRepository : ISensitiveEntityRepository +{ + /// + /// Get a number of unread ChatMessages with specified WorkshopId. + /// + /// Workshop's key. + /// A representing the result of the asynchronous operation. The task result contains a that contains the number of unread messages for the specified Workshop. + Task CountUnreadMessagesAsync(Guid workshopId); +} \ No newline at end of file diff --git a/OutOfSchool/OutOfSchool.WebApi.Tests/Controllers/WorkshopControllerTests.cs b/OutOfSchool/OutOfSchool.WebApi.Tests/Controllers/WorkshopControllerTests.cs index b3eddc10bf..f165e4241c 100644 --- a/OutOfSchool/OutOfSchool.WebApi.Tests/Controllers/WorkshopControllerTests.cs +++ b/OutOfSchool/OutOfSchool.WebApi.Tests/Controllers/WorkshopControllerTests.cs @@ -47,6 +47,7 @@ public class WorkshopControllerTests private string userId; private Mock httpContextMoq; private List workshopBaseCards; + private List workshopProviderViewCards; private List workshopShortEntitiesList; private List workshopProviderViewCardList; @@ -65,6 +66,8 @@ public void OneTimeSetup() provider = WithProvider(); workshopCards = WithWorkshopCards(); workshopBaseCards = WorkshopBaseCardGenerator.Generate(5); + workshopProviderViewCards = WorkshopProviderViewCardGenerator.Generate(5); + workshopShortEntitiesList = ShortEntityDtoGenerator.Generate(10); workshopProviderViewCardList = WithWorkshopProviderViewCards(); @@ -132,8 +135,8 @@ public async Task GetByProviderId_WhenThereAreWorkshops_ShouldReturnOkResultObje { // Arrange var filter = new ExcludeIdFilter() { From = 0, Size = int.MaxValue }; - var searchResult = new SearchResult() { TotalAmount = 5, Entities = workshopBaseCards }; - workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) + var searchResult = new SearchResult() { TotalAmount = 5, Entities = workshopProviderViewCards }; + workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) .ReturnsAsync(searchResult); // Act @@ -143,7 +146,7 @@ public async Task GetByProviderId_WhenThereAreWorkshops_ShouldReturnOkResultObje workshopServiceMoq.VerifyAll(); Assert.That(result, Is.Not.Null); Assert.AreEqual(Ok, result.StatusCode); - Assert.AreEqual(workshops.Count, (result.Value as SearchResult).TotalAmount); + Assert.AreEqual(workshops.Count, (result.Value as SearchResult).TotalAmount); } [Test] @@ -151,8 +154,8 @@ public async Task GetByProviderId_WhenThereIsNoWorkshops_ShouldReturnNoContentRe { // Arrange var filter = new ExcludeIdFilter() { From = 0, Size = int.MaxValue }; - var emptySearchResult = new SearchResult() { TotalAmount = 0, Entities = new List() }; - workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) + var emptySearchResult = new SearchResult() { TotalAmount = 0, Entities = new List() }; + workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) .ReturnsAsync(emptySearchResult); // Act @@ -182,8 +185,8 @@ public async Task GetByProviderId_WhenThereIsExcludedId_ShouldReturnOkResultObje var expectedWorkshopCount = workshopBaseCards.Count - 1; var excludedId = workshopBaseCards.FirstOrDefault().WorkshopId; var filter = new ExcludeIdFilter() { From = 0, Size = int.MaxValue, ExcludedId = excludedId }; - var searchResult = new SearchResult() { TotalAmount = 4, Entities = workshopBaseCards.Skip(1).ToList() }; - workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) + var searchResult = new SearchResult() { TotalAmount = 4, Entities = workshopProviderViewCards.Skip(1).ToList() }; + workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) .ReturnsAsync(searchResult); // Act @@ -193,7 +196,7 @@ public async Task GetByProviderId_WhenThereIsExcludedId_ShouldReturnOkResultObje workshopServiceMoq.VerifyAll(); Assert.That(result, Is.Not.Null); Assert.AreEqual(Ok, result.StatusCode); - Assert.AreEqual(expectedWorkshopCount, (result.Value as SearchResult).TotalAmount); + Assert.AreEqual(expectedWorkshopCount, (result.Value as SearchResult).TotalAmount); } #endregion @@ -323,8 +326,8 @@ public async Task GetWorkshopProviderViewCardsByProviderId_WhenThereAreWorkshops { // Arrange var filter = new ExcludeIdFilter() { From = 0, Size = int.MaxValue }; - var searchResult = new SearchResult() { TotalAmount = 5, Entities = workshopProviderViewCardList }; - workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) + var searchResult = new SearchResult() { TotalAmount = 5, Entities = workshopProviderViewCards }; + workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) .ReturnsAsync(searchResult); // Act @@ -343,7 +346,7 @@ public async Task GetWorkshopProviderViewCardsByProviderId_WhenThereIsNoWorkshop // Arrange var filter = new ExcludeIdFilter() { From = 0, Size = int.MaxValue }; var emptySearchResult = new SearchResult() { TotalAmount = 0, Entities = new List() }; - workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) + workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) .ReturnsAsync(emptySearchResult); // Act @@ -363,7 +366,7 @@ public async Task GetWorkshopProviderViewCardsByProviderId_WhenSizeFilterIsProvi var filter = new ExcludeIdFilter() { From = 0, Size = expectedCount }; var expectedTotalAmount = 5; var searchResult = new SearchResult() { TotalAmount = expectedTotalAmount, Entities = workshopProviderViewCardList.Take(expectedCount).ToList() }; - workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) + workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) .ReturnsAsync(searchResult); // Act @@ -387,7 +390,7 @@ public async Task GetWorkshopProviderViewCardsByProviderId_WhenFromFilterIsProvi var expectedResult = workshopProviderViewCardList.Skip(skipCount).Take(expectedCount).ToList(); var filter = new ExcludeIdFilter() { From = skipCount, Size = expectedCount }; var searchResult = new SearchResult() { TotalAmount = expectedTotalAmount, Entities = expectedResult }; - workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) + workshopServiceMoq.Setup(x => x.GetByProviderId(It.IsAny(), It.IsAny())) .ReturnsAsync(searchResult); // Act diff --git a/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatMessageWorkshopServiceTests.cs b/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatMessageWorkshopServiceTests.cs index 27d003bf23..1eb501262f 100644 --- a/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatMessageWorkshopServiceTests.cs +++ b/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatMessageWorkshopServiceTests.cs @@ -35,7 +35,7 @@ public class ChatMessageWorkshopServiceTests WorkshopId = Guid.NewGuid(), }; - private IEntityRepository messageRepository; + private IChatMessageRepository messageRepository; private Mock roomServiceMock; private Mock> workshopHub; private Mock> loggerMock; @@ -60,7 +60,7 @@ public void SetUp() options = builder.Options; dbContext = new OutOfSchoolDbContext(options); - messageRepository = new EntityRepository(dbContext); + messageRepository = new ChatMessageRepository(dbContext); roomServiceMock = new Mock(); workshopHub = new Mock>(); loggerMock = new Mock>(); @@ -195,6 +195,51 @@ public async Task GetMessagesForChatRoomAndSetReadDateTimeIfItIsNullAsync_WhenCa } #endregion + #region CountUnreadMessagesAsync + [Test] + public async Task CountUnreadMessagesAsync_WhenCalledWithValidWorkshopId_ShouldReturnNumberOfUnreadMessages() + { + // Arrange + var workshopId = Guid.NewGuid(); + int expectedUnreadMessages = 7; + var messageRepositoryMock = new Mock(); + messageRepositoryMock.Setup(x => x.CountUnreadMessagesAsync(workshopId)).Returns(Task.FromResult(expectedUnreadMessages)); + var messageService = new ChatMessageWorkshopService( + messageRepositoryMock.Object, + roomServiceMock.Object, + workshopHub.Object, + loggerMock.Object, + mapper); + + // Act + var result = await messageService.CountUnreadMessagesAsync(workshopId).ConfigureAwait(false); + + // Assert + Assert.AreEqual(expectedUnreadMessages, result); + } + + [Test] + public void CountUnreadMessagesAsync_ThrowsException() + { + // Arrange + var exceptionText = "Test exception"; + var workshopId = Guid.NewGuid(); + var messageRepositoryMock = new Mock(); + messageRepositoryMock.Setup(x => x.CountUnreadMessagesAsync(workshopId)) + .Throws(new Exception(exceptionText)); + var messageService = new ChatMessageWorkshopService( + messageRepositoryMock.Object, + roomServiceMock.Object, + workshopHub.Object, + loggerMock.Object, + mapper); + + // Act and Assert + Exception ex = Assert.ThrowsAsync(async () => await messageService.CountUnreadMessagesAsync(workshopId)); + Assert.That(ex.Message, Is.EqualTo(exceptionText)); + } + #endregion + private void SeedDatabase() { using var context = new OutOfSchoolDbContext(options); diff --git a/OutOfSchool/OutOfSchool.WebApi.Tests/Services/Database/WorkshopServiceTests.cs b/OutOfSchool/OutOfSchool.WebApi.Tests/Services/Database/WorkshopServiceTests.cs index 46ba30064b..8b7eaf6586 100644 --- a/OutOfSchool/OutOfSchool.WebApi.Tests/Services/Database/WorkshopServiceTests.cs +++ b/OutOfSchool/OutOfSchool.WebApi.Tests/Services/Database/WorkshopServiceTests.cs @@ -40,6 +40,7 @@ public class WorkshopServiceTests private Mock providerAdminRepository; private Mock averageRatingServiceMock; private Mock providerRepositoryMock; + private Mock chatMessageWorkshopServiceMock; [SetUp] public void SetUp() @@ -54,6 +55,7 @@ public void SetUp() providerAdminRepository = new Mock(); averageRatingServiceMock = new Mock(); providerRepositoryMock = new Mock(); + chatMessageWorkshopServiceMock = new Mock(); workshopService = new WorkshopService( @@ -65,7 +67,8 @@ public void SetUp() workshopImagesMediator.Object, providerAdminRepository.Object, averageRatingServiceMock.Object, - providerRepositoryMock.Object); + providerRepositoryMock.Object, + chatMessageWorkshopServiceMock.Object); } #region Create @@ -190,34 +193,34 @@ public async Task GetByProviderId_WhenProviderWithIdExists_ShouldReturnEntities( { // Arrange var workshops = WithWorkshopsList().ToList(); - var expectedWorkshopBaseCards = workshops.Select(w => new WorkshopBaseCard() { ProviderId = w.ProviderId, WorkshopId = w.Id, }).ToList(); + var expectedWorkshopBaseCards = workshops.Select(w => new WorkshopProviderViewCard() { ProviderId = w.ProviderId, WorkshopId = w.Id, }).ToList(); SetupGetRepositoryCount(10); SetupGetByProviderById(workshops); - mapperMock.Setup(m => m.Map>(It.IsAny>())).Returns(expectedWorkshopBaseCards); + mapperMock.Setup(m => m.Map>(It.IsAny>())).Returns(expectedWorkshopBaseCards); // Act - var result = await workshopService.GetByProviderId(It.IsAny(), It.IsAny()).ConfigureAwait(false); + var result = await workshopService.GetByProviderId(It.IsAny(), It.IsAny()).ConfigureAwait(false); // Assert workshopRepository.VerifyAll(); mapperMock.VerifyAll(); - (result as SearchResult).TotalAmount.Should().Be(workshops.Count); - (result as SearchResult).Entities.Should().BeEquivalentTo(expectedWorkshopBaseCards); + (result as SearchResult).TotalAmount.Should().Be(workshops.Count); + (result as SearchResult).Entities.Should().BeEquivalentTo(expectedWorkshopBaseCards); } [Test] public async Task GetByProviderId_WhenThereIsNoEntityWithId_ShouldReturnEmptyList() { // Arrange - var emptyListWorkshopCards = new List(); + var emptyListWorkshopCards = new List(); SetupGetRepositoryCount(0); SetupGetByProviderById(new List()); - mapperMock.Setup(m => m.Map>(It.IsAny>())).Returns(emptyListWorkshopCards); + mapperMock.Setup(m => m.Map>(It.IsAny>())).Returns(emptyListWorkshopCards); // Act - var result = await workshopService.GetByProviderId(Guid.NewGuid(), It.IsAny()).ConfigureAwait(false); + var result = await workshopService.GetByProviderId(Guid.NewGuid(), It.IsAny()).ConfigureAwait(false); // Assert workshopRepository.VerifyAll(); @@ -232,22 +235,22 @@ public async Task GetByProviderId_WhenThereIsExcludedIds_ShouldReturnList() // Arrange var workshops = WithWorkshopsList().ToList(); var excludedIds = new List() { new Guid("b94f1989-c4e7-4878-ac86-21c4a402fb43"), new Guid("8c14044b-e30d-4b14-a18b-5b3b859ad676") }; - var expectedWorkshopBaseCards = workshops.Select(w => new WorkshopBaseCard() { ProviderId = w.ProviderId }) + var expectedWorkshopBaseCards = workshops.Select(w => new WorkshopProviderViewCard() { ProviderId = w.ProviderId }) .Where(w => !excludedIds.Any(excluded => w.WorkshopId != excluded)).ToList(); SetupGetRepositoryCount(10); SetupGetByProviderById(workshops); - mapperMock.Setup(m => m.Map>(It.IsAny>())).Returns(expectedWorkshopBaseCards); + mapperMock.Setup(m => m.Map>(It.IsAny>())).Returns(expectedWorkshopBaseCards); // Act - var result = await workshopService.GetByProviderId(It.IsAny(), It.IsAny()).ConfigureAwait(false); + var result = await workshopService.GetByProviderId(It.IsAny(), It.IsAny()).ConfigureAwait(false); // Assert workshopRepository.VerifyAll(); mapperMock.VerifyAll(); - (result as SearchResult).TotalAmount.Should().Be(workshops.Count); - (result as SearchResult).Entities.Should().BeEquivalentTo(expectedWorkshopBaseCards); + (result as SearchResult).TotalAmount.Should().Be(workshops.Count); + (result as SearchResult).Entities.Should().BeEquivalentTo(expectedWorkshopBaseCards); } #endregion diff --git a/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/WorkshopController.cs b/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/WorkshopController.cs index 90245b596c..09abe00ad2 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/WorkshopController.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/WorkshopController.cs @@ -135,14 +135,14 @@ public async Task GetWorkshopListByProviderAdminId(string provide /// Provider's id. /// Filter to get specified portion of workshop view cards for specified provider. /// Id of the excluded workshop could be specified. - /// , or no content. + /// , or no content. /// The list of found entities by given Id. /// No entity with given Id was found. /// Provider id is empty. /// If any server error occures. For example: Id was less than one. [AllowAnonymous] [HttpGet("{id}")] - [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(SearchResult))] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(SearchResult))] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] @@ -153,7 +153,7 @@ public async Task GetByProviderId(Guid id, [FromQuery] ExcludeIdF return BadRequest("Provider id is empty."); } - var workshopCards = await combinedWorkshopService.GetByProviderId(id, filter) + var workshopCards = await combinedWorkshopService.GetByProviderId(id, filter) .ConfigureAwait(false); if (workshopCards.TotalAmount == 0) @@ -183,7 +183,7 @@ public async Task GetWorkshopProviderViewCardsByProviderId(Guid i return BadRequest("Provider id is empty."); } - var workshopProviderViewCards = await combinedWorkshopService.GetByProviderId(id, filter).ConfigureAwait(false); + var workshopProviderViewCards = await combinedWorkshopService.GetByProviderId(id, filter).ConfigureAwait(false); if (workshopProviderViewCards.TotalAmount == 0) { diff --git a/OutOfSchool/OutOfSchool.WebApi/Controllers/V2/WorkshopController.cs b/OutOfSchool/OutOfSchool.WebApi/Controllers/V2/WorkshopController.cs index 203a5a3968..7d995e1121 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Controllers/V2/WorkshopController.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Controllers/V2/WorkshopController.cs @@ -72,18 +72,18 @@ public async Task GetById(Guid id) /// /// Provider's id. /// Filter to get specified portion of workshops for specified provider. Ids of the excluded workshops could be specified. - /// , or no content. + /// , or no content. /// The list of found entities by given Id. /// No entity with given Id was found. /// If any server error occures. For example: Id was less than one. [AllowAnonymous] [HttpGet("{id}")] - [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(SearchResult))] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(SearchResult))] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task GetByProviderId(Guid id, [FromQuery] ExcludeIdFilter filter) { - var workshopCards = await combinedWorkshopService.GetByProviderId(id, filter).ConfigureAwait(false); + var workshopCards = await combinedWorkshopService.GetByProviderId(id, filter).ConfigureAwait(false); if (workshopCards.TotalAmount == 0) { diff --git a/OutOfSchool/OutOfSchool.WebApi/Models/Workshop/WorkshopCard.cs b/OutOfSchool/OutOfSchool.WebApi/Models/Workshop/WorkshopCard.cs index 3bffbd2b87..966779ad4c 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Models/Workshop/WorkshopCard.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Models/Workshop/WorkshopCard.cs @@ -78,4 +78,6 @@ public class WorkshopProviderViewCard : WorkshopBaseCard public int AmountOfPendingApplications { get; set; } public WorkshopStatus Status { get; set; } + + public int UnreadMessages { get; set; } } \ No newline at end of file diff --git a/OutOfSchool/OutOfSchool.WebApi/Services/ChatMessageWorkshopService.cs b/OutOfSchool/OutOfSchool.WebApi/Services/ChatMessageWorkshopService.cs index 6ddb44bf84..5cc418c3c7 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Services/ChatMessageWorkshopService.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Services/ChatMessageWorkshopService.cs @@ -1,8 +1,11 @@ using System.Linq.Expressions; using AutoMapper; using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore.Storage; +using Nest; using Newtonsoft.Json; using OutOfSchool.Services.Enums; +using OutOfSchool.Services.Repository; using OutOfSchool.WebApi.Models; using OutOfSchool.WebApi.Models.ChatWorkshop; @@ -13,7 +16,7 @@ namespace OutOfSchool.WebApi.Services; /// public class ChatMessageWorkshopService : IChatMessageWorkshopService { - private readonly IEntityRepository messageRepository; + private readonly IChatMessageRepository messageRepository; private readonly IChatRoomWorkshopService roomService; private readonly IHubContext workshopHub; private readonly ILogger logger; @@ -28,7 +31,7 @@ public class ChatMessageWorkshopService : IChatMessageWorkshopService /// Logger. /// Mapper. public ChatMessageWorkshopService( - IEntityRepository chatMessageRepository, + IChatMessageRepository chatMessageRepository, IChatRoomWorkshopService roomRepository, IHubContext workshopHub, ILogger logger, @@ -124,6 +127,19 @@ public async Task> GetMessagesForChatRoomAndSetRead } } + public async Task CountUnreadMessagesAsync(Guid workshopId) + { + try + { + return await messageRepository.CountUnreadMessagesAsync(workshopId).ConfigureAwait(false); + } + catch (Exception exception) + { + logger.LogError($"Getting number of unread messages with {nameof(workshopId)}:{workshopId} failed. Exception: {exception.Message}"); + throw; + } + } + private async Task> GetMessagesForChatRoomDomainModelAsync(Guid chatRoomId, OffsetFilter offsetFilter) { if (offsetFilter is null) diff --git a/OutOfSchool/OutOfSchool.WebApi/Services/Database/IWorkshopService.cs b/OutOfSchool/OutOfSchool.WebApi/Services/Database/IWorkshopService.cs index dfc651fe19..73c392be6a 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Services/Database/IWorkshopService.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Services/Database/IWorkshopService.cs @@ -103,8 +103,7 @@ public interface IWorkshopService /// Type of entity that must be return. /// A representing the result of the asynchronous operation. /// The task result contains a that contains elements from the input sequence. - Task> GetByProviderId(Guid id, ExcludeIdFilter filter) - where T : WorkshopBaseCard; + Task> GetByProviderId(Guid id, ExcludeIdFilter filter); /// /// Get entities from the database that match filter's parameters. diff --git a/OutOfSchool/OutOfSchool.WebApi/Services/Database/WorkshopService.cs b/OutOfSchool/OutOfSchool.WebApi/Services/Database/WorkshopService.cs index f5a3051d1b..6f8c9d9361 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Services/Database/WorkshopService.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Services/Database/WorkshopService.cs @@ -4,6 +4,7 @@ using Castle.Core.Internal; using H3Lib; using H3Lib.Extensions; +using Microsoft.Extensions.Logging; using Nest; using OutOfSchool.Common.Enums; using OutOfSchool.Services.Enums; @@ -38,6 +39,7 @@ public class WorkshopService : IWorkshopService private readonly IProviderAdminRepository providerAdminRepository; private readonly IAverageRatingService averageRatingService; private readonly IProviderRepository providerRepository; + private readonly IChatMessageWorkshopService chatMessageWorkshopService; /// /// Initializes a new instance of the class. @@ -61,7 +63,8 @@ public WorkshopService( IImageDependentEntityImagesInteractionService workshopImagesService, IProviderAdminRepository providerAdminRepository, IAverageRatingService averageRatingService, - IProviderRepository providerRepository) + IProviderRepository providerRepository, + IChatMessageWorkshopService chatMessageWorkshopService) { this.workshopRepository = workshopRepository; this.dateTimeRangeRepository = dateTimeRangeRepository; @@ -72,6 +75,7 @@ public WorkshopService( this.providerAdminRepository = providerAdminRepository; this.averageRatingService = averageRatingService; this.providerRepository = providerRepository; + this.chatMessageWorkshopService = chatMessageWorkshopService; } /// @@ -254,8 +258,7 @@ public async Task> GetWorkshopListByProviderAdminId(string } /// - public async Task> GetByProviderId(Guid id, ExcludeIdFilter filter) - where T : WorkshopBaseCard + public async Task> GetByProviderId(Guid id, ExcludeIdFilter filter) { logger.LogInformation($"Getting Workshop by organization started. Looking ProviderId = {id}."); @@ -280,11 +283,22 @@ public async Task> GetByProviderId(Guid id, ExcludeIdFilter f ? $"There aren't Workshops for Provider with Id = {id}." : $"From Workshop table were successfully received {workshops.Count} records."); - var workshopBaseCards = mapper.Map>(workshops).ToList(); - var result = new SearchResult() + var workshopBaseCards = mapper.Map>(workshops).ToList(); + + if (workshopBaseCards.Any()) + { + foreach (var workshop in workshopBaseCards) + { + var messages = await chatMessageWorkshopService.CountUnreadMessagesAsync(workshop.WorkshopId).ConfigureAwait(false); + logger.LogInformation($"Workshop.Id: {workshop.WorkshopId} has {messages} unread messages."); + workshop.UnreadMessages = messages; + } + } + + var result = new SearchResult() { TotalAmount = workshopBaseCardsCount, - Entities = await GetWorkshopsWithAverageRating(workshopBaseCards).ConfigureAwait(false), + Entities = await GetWorkshopsWithAverageRating(workshopBaseCards).ConfigureAwait(false), }; return result; diff --git a/OutOfSchool/OutOfSchool.WebApi/Services/IChatMessageWorkshopService.cs b/OutOfSchool/OutOfSchool.WebApi/Services/IChatMessageWorkshopService.cs index 68a9ae7edc..faa098465f 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Services/IChatMessageWorkshopService.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Services/IChatMessageWorkshopService.cs @@ -40,4 +40,11 @@ public interface IChatMessageWorkshopService /// Role of current user. /// A representing the result of the asynchronous operation. The task result contains a that contains elements from the input sequence. Task> GetMessagesForChatRoomAndSetReadDateTimeIfItIsNullAsync(Guid chatRoomId, OffsetFilter offsetFilter, Role userRole); + + /// + /// Get a number of unread ChatMessages with specified WorkshopId. + /// + /// Workshop's key. + /// A representing the result of the asynchronous operation. The task result contains a that contains the number of unread messages for the specified Workshop. + Task CountUnreadMessagesAsync(Guid workshopId); } \ No newline at end of file diff --git a/OutOfSchool/OutOfSchool.WebApi/Services/IWorkshopServicesCombiner.cs b/OutOfSchool/OutOfSchool.WebApi/Services/IWorkshopServicesCombiner.cs index d6c2227767..3ddb5cd79a 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Services/IWorkshopServicesCombiner.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Services/IWorkshopServicesCombiner.cs @@ -82,8 +82,7 @@ public interface IWorkshopServicesCombiner /// Type of entity that must be return. /// A representing the result of the asynchronous operation. /// The task result contains a that contains elements from the input sequence. - Task> GetByProviderId(Guid id, ExcludeIdFilter filter) - where T : WorkshopBaseCard; + Task> GetByProviderId(Guid id, ExcludeIdFilter filter); /// /// Get all entities that matches filter's parameters. diff --git a/OutOfSchool/OutOfSchool.WebApi/Services/ProviderAdminService.cs b/OutOfSchool/OutOfSchool.WebApi/Services/ProviderAdminService.cs index 4defed92cd..f06f3fa1af 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Services/ProviderAdminService.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Services/ProviderAdminService.cs @@ -361,7 +361,7 @@ public async Task> GetWorkshopsThatProvid } var filter = new ExcludeIdFilter() { From = 0, Size = int.MaxValue }; - return await workshopService.GetByProviderId(providerAdmin.ProviderId, filter).ConfigureAwait(false); + return await workshopService.GetByProviderId(providerAdmin.ProviderId, filter).ConfigureAwait(false); } var pa = providersAdmins.SingleOrDefault(); diff --git a/OutOfSchool/OutOfSchool.WebApi/Services/WorkshopServicesCombiner.cs b/OutOfSchool/OutOfSchool.WebApi/Services/WorkshopServicesCombiner.cs index ae8433b123..c71c4492db 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Services/WorkshopServicesCombiner.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Services/WorkshopServicesCombiner.cs @@ -228,10 +228,9 @@ public async Task> GetWorkshopListByProviderAdminId(string } /// - public async Task> GetByProviderId(Guid id, ExcludeIdFilter filter) - where T : WorkshopBaseCard + public async Task> GetByProviderId(Guid id, ExcludeIdFilter filter) { - var workshopBaseCards = await workshopService.GetByProviderId(id, filter).ConfigureAwait(false); + var workshopBaseCards = await workshopService.GetByProviderId(id, filter).ConfigureAwait(false); return workshopBaseCards; } diff --git a/OutOfSchool/OutOfSchool.WebApi/Startup.cs b/OutOfSchool/OutOfSchool.WebApi/Startup.cs index 9d0d9d5a78..570d38fe61 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Startup.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Startup.cs @@ -211,8 +211,8 @@ public static void AddApplicationServices(this WebApplicationBuilder builder) // entities services services.AddTransient(); - services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -279,6 +279,7 @@ public static void AddApplicationServices(this WebApplicationBuilder builder) services .AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/OutOfSchool/OutOfSchool.WebApi/Util/MappingProfile.cs b/OutOfSchool/OutOfSchool.WebApi/Util/MappingProfile.cs index f0d0e116e3..b72864b0ae 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Util/MappingProfile.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Util/MappingProfile.cs @@ -235,7 +235,8 @@ public MappingProfile() src.Applications.Count(x => x.Status == ApplicationStatus.Approved || x.Status == ApplicationStatus.StudyingForYears))) - .ForMember(dest => dest.Address, opt => opt.Ignore()); + .ForMember(dest => dest.Address, opt => opt.Ignore()) + .ForMember(dest => dest.UnreadMessages, opt => opt.Ignore()); CreateMap().ReverseMap(); diff --git a/OutOfSchool/Tests/OutOfSchool.Tests.Common/TestDataGenerators/WorkshopProviderViewCardGenerator.cs b/OutOfSchool/Tests/OutOfSchool.Tests.Common/TestDataGenerators/WorkshopProviderViewCardGenerator.cs new file mode 100644 index 0000000000..dd908f3fc5 --- /dev/null +++ b/OutOfSchool/Tests/OutOfSchool.Tests.Common/TestDataGenerators/WorkshopProviderViewCardGenerator.cs @@ -0,0 +1,33 @@ +using Bogus; +using OutOfSchool.Common.Enums; +using OutOfSchool.WebApi.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OutOfSchool.Tests.Common.TestDataGenerators; +public static class WorkshopProviderViewCardGenerator +{ + private static readonly Faker Faker = new Faker() + .RuleFor(x => x.Title, f => f.Company.CompanyName()) + .RuleFor(x => x.MinAge, f => f.Random.Number(1, 18)) + .RuleFor(x => x.MaxAge, f => f.Random.Number(1, 18)) + .RuleFor(x => x.Price, f => f.Random.Decimal(0, 10000)) + .RuleFor(x => x.WithDisabilityOptions, f => f.Random.Bool()) + .RuleFor(x => x.CoverImageId, f => f.Image.LoremFlickrUrl()) + .RuleFor(x => x.ProviderTitle, f => f.Company.CompanyName()) + .RuleFor(x => x.ProviderOwnership, f => f.PickRandom()) + .RuleFor(x => x.PayRate, f => f.PickRandom()) + .RuleFor(x => x.ProviderId, _ => Guid.NewGuid()) + .RuleFor(x => x.WorkshopId, _ => Guid.NewGuid()) + .RuleFor(x => x.AvailableSeats, f => f.Random.UInt(0, 100)) + .RuleFor(x => x.TakenSeats, f => f.Random.UInt(0, 100)) + .RuleFor(x => x.AmountOfPendingApplications, f => f.Random.Int(0, 100)) + .RuleFor(x => x.UnreadMessages, f => f.Random.Int(0, 100)); + + public static WorkshopProviderViewCard Generate() => Faker.Generate(); + + public static List Generate(int count) => new List(count); +}