diff --git a/OutOfSchool/OutOfSchool.WebApi.Tests/Hubs/ChatWorkshopHubTests.cs b/OutOfSchool/OutOfSchool.WebApi.Tests/Hubs/ChatWorkshopHubTests.cs index 18aac55c35..ba6000eed7 100644 --- a/OutOfSchool/OutOfSchool.WebApi.Tests/Hubs/ChatWorkshopHubTests.cs +++ b/OutOfSchool/OutOfSchool.WebApi.Tests/Hubs/ChatWorkshopHubTests.cs @@ -163,10 +163,12 @@ public async Task SendMessageToOthersInGroup_WhenUserSetsNotOwnParentId_ShouldWr var invalidParentId = Guid.NewGuid(); - var chatNewMessage = $"{{'workshopId':'{Guid.NewGuid()}', 'parentId':'{invalidParentId}', 'text':'hi', 'senderRoleIsProvider':false}}"; + var chatNewMessage = $"{{'workshopId':'{Guid.NewGuid()}', 'parentId':'{invalidParentId}', 'chatRoomId':'{Guid.NewGuid()}', 'text':'hi', 'senderRoleIsProvider':false}}"; validationServiceMock.Setup(x => x.UserIsParentOwnerAsync(UserId, invalidParentId)).ReturnsAsync(false); + roomServiceMock.Setup(x => x.GetByIdAsync(It.IsAny())).ReturnsAsync(new ChatRoomWorkshopDto()); + clientsMock.Setup(clients => clients.Caller).Returns(clientProxyMock.Object); // Act @@ -187,10 +189,13 @@ public async Task SendMessageToOthersInGroup_WhenParamsAreValidAndChatRoomExists var validWorkshopId = Guid.NewGuid(); var validParentId = Guid.NewGuid(); - var validNewMessage = $"{{'workshopId':'{validWorkshopId}', 'parentId':'{validParentId}', 'text':'hi', 'senderRoleIsProvider':true}}"; + var validChatRoomId = Guid.NewGuid(); + var validNewMessage = $"{{'workshopId':'{validWorkshopId}', 'parentId':'{validParentId}', 'chatRoomId':'{validChatRoomId}', 'text':'hi', 'senderRoleIsProvider':true}}"; validationServiceMock.Setup(x => x.UserIsWorkshopOwnerAsync(UserId, validWorkshopId, Subrole.None)).ReturnsAsync(true); + roomServiceMock.Setup(x => x.GetByIdAsync(validChatRoomId)).ReturnsAsync(new ChatRoomWorkshopDto()); + var validCreatedMessage = new ChatMessageWorkshopDto() { Id = Guid.NewGuid(), @@ -227,54 +232,20 @@ public async Task SendMessageToOthersInGroup_WhenParamsAreValidAndChatRoomExists } [Test] - public async Task SendMessageToOthersInGroup_WhenParamsAreValidAndChatRoomDoesNotExists_ShouldSaveMessageAndSendMessageToGroup() + public async Task SendMessageToOthersInGroup_WhenChatRoomDoesNotExist_ShouldWriteMessageToCallerWithException() { // Arrange - var userRole = Role.Parent.ToString(); - hubCallerContextMock.Setup(x => x.User.FindFirst(IdentityResourceClaimsTypes.Role)) - .Returns(new Claim(IdentityResourceClaimsTypes.Role, userRole)); - - var validParentId = Guid.NewGuid(); - - var validNewMessage = $"{{'workshopId':'{Guid.NewGuid()}', 'parentId':'{validParentId}', 'text':'hi', 'senderRoleIsProvider':false}}"; - - validationServiceMock.Setup(x => x.UserIsParentOwnerAsync(UserId, validParentId)).ReturnsAsync(true); - - var validChatRoom = new ChatRoomWorkshopDto() { Id = Guid.NewGuid(), WorkshopId = Guid.NewGuid(), ParentId = validParentId, }; - roomServiceMock.Setup(x => x.CreateOrReturnExistingAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(validChatRoom); - - var validCreatedMessage = new ChatMessageWorkshopDto() - { - Id = Guid.NewGuid(), - SenderRoleIsProvider = false, - Text = "hi", - ChatRoomId = validChatRoom.Id, - CreatedDateTime = DateTimeOffset.UtcNow, - ReadDateTime = null, - }; - messageServiceMock.Setup(x => x.CreateAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(validCreatedMessage); - - var validParent = new Parent() { Id = validParentId, UserId = UserId }; - parentRepositoryMock.Setup(x => x.GetById(validParentId)).ReturnsAsync(validParent); - - var validWorkshops = new List() { new Workshop() { Id = validChatRoom.WorkshopId, Provider = new Provider() { UserId = "someId" } } }; - workshopRepositoryMock.Setup(x => x.GetByFilter(It.IsAny>>(), It.IsAny())).ReturnsAsync(validWorkshops); - - groupsMock.Setup(x => x.AddToGroupAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.CompletedTask); + var chatNewMessage = $"{{'workshopId':'{Guid.NewGuid()}', 'parentId':'{Guid.NewGuid()}', 'chatRoomId':'{Guid.NewGuid()}', 'text':'hi', 'senderRoleIsProvider':false}}"; - clientsMock.Setup(clients => clients.Group(It.IsAny())).Returns(clientProxyMock.Object); + roomServiceMock.Setup(x => x.GetByIdAsync(It.IsAny())).ReturnsAsync(null as ChatRoomWorkshopDto); - var validProviderAdmins = new List(); - providerAdminRepositoryMock.Setup(x => x.GetByFilter(It.IsAny>>(), It.IsAny())).ReturnsAsync(validProviderAdmins); + clientsMock.Setup(clients => clients.Caller).Returns(clientProxyMock.Object); // Act - await chatHub.SendMessageToOthersInGroupAsync(validNewMessage).ConfigureAwait(false); + await chatHub.SendMessageToOthersInGroupAsync(chatNewMessage).ConfigureAwait(false); // Assert - messageServiceMock.Verify(x => x.CreateAsync(It.IsAny(), It.IsAny()), Times.Once); - clientsMock.Verify(clients => clients.Group(validChatRoom.Id.ToString()), Times.Once); + clientsMock.Verify(clients => clients.Caller, Times.Once); + clientsMock.Verify(clients => clients.Group(It.IsAny()), Times.Never); } } \ No newline at end of file diff --git a/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ApplicationServiceTests.cs b/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ApplicationServiceTests.cs index 129609a64c..768176f8f7 100644 --- a/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ApplicationServiceTests.cs +++ b/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ApplicationServiceTests.cs @@ -106,7 +106,7 @@ public async Task GetApplications_WhenCalled_ShouldReturnApplications() var result = await service.GetAll(new ApplicationFilter()); // Assert - Assert.AreEqual(result.Entities.Count, application.Count()); + Assert.AreEqual(result.Entities.Count, application.Count); } [Test] @@ -300,7 +300,7 @@ public void CreateApplication_WhenModelIsNull_ShouldThrowArgumentException() } [Test] - public void CreateApplication_WhenLimitIsExceeded_ShouldThrowArgumentException() + public async Task CreateApplication_WhenLimitIsExceeded_ShouldThrowArgumentException() { // Arrange var application = new ApplicationCreate() @@ -308,8 +308,25 @@ public void CreateApplication_WhenLimitIsExceeded_ShouldThrowArgumentException() WorkshopId = new Guid("0083633f-4e5b-4c09-a89d-52d8a9b89cdb"), }; - // Act and Assert - service.Invoking(w => w.Create(application)).Should().ThrowAsync(); + currentUserServiceMock.Setup(x => x.UserHasRights(It.IsAny())).Returns(() => Task.FromResult(true)); + + workshopServiceCombinerMock.Setup(x => x.GetById(application.WorkshopId)).Returns(Task.FromResult(new WorkshopDto())); + + var applications = new List + { + new Application(), + new Application(), + new Application(), + }; + + applicationRepositoryMock.Setup(x => x.GetByFilter(It.IsAny>>(), It.IsAny())).ReturnsAsync(applications.AsEnumerable()); + + // Act + var result = await service.Create(application); + + // Assert + Assert.That(result.Model is null); + Assert.That(result.Description != string.Empty); } [Test] @@ -519,14 +536,12 @@ public async Task GetAllByChild_WhenIdIsNotValid_ShouldReturnEmptyCollection() Assert.That(result, Is.Null); } - [Test] public async Task UpdateApplication_WhenIdIsValid_ShouldReturnApplication() { // Arrange var id = new Guid("1745d16a-6181-43d7-97d0-a1d6cc34a8bd"); var changedEntity = WithApplication(id); - var userId = Guid.NewGuid().ToString(); var applicationsMock = WithApplicationsList().AsQueryable().BuildMock(); @@ -587,7 +602,6 @@ public async Task UpdateApplication_WhenIdIsValidAndNeedUpdateWorkshopStatus_Sho var id = new Guid("1745d16a-6181-43d7-97d0-a1d6cc34a8bd"); var entity = WithApplication(id); var changedEntity = WithApplication(id, ApplicationStatus.Approved); - var userId = Guid.NewGuid().ToString(); var workshop = WithWorkshop(new Guid("0083633f-4e5b-4c09-a89d-52d8a9b89cdb")); applicationRepositoryMock.Setup(a => a.Update(It.IsAny(), It.IsAny>())) @@ -627,7 +641,7 @@ public async Task UpdateApplication_WhenIdIsValidAndNeedUpdateWorkshopStatus_Sho workshopRepositoryMock.Setup(w => w.GetAvailableSeats(It.IsAny())).ReturnsAsync(uint.MaxValue); currentUserServiceMock.Setup(c => c.UserRole).Returns("provider"); - currentUserServiceMock.Setup(c => c.UserSubRole).Returns(""); + currentUserServiceMock.Setup(c => c.UserSubRole).Returns(string.Empty); // Act var result = await service.Update(update, Guid.NewGuid()).ConfigureAwait(false); @@ -640,7 +654,6 @@ public async Task UpdateApplication_WhenIdIsValidAndNeedUpdateWorkshopStatus_Sho public async Task UpdateApplication_WhenThereIsNoApplicationWithId_ShouldReturnNotSucceeded() { // Arrange - var userId = Guid.NewGuid().ToString(); var application = new ApplicationUpdate() { Id = new Guid("1745d16a-6181-43d7-97d0-a1d6cc34a8bd"), @@ -659,7 +672,6 @@ public async Task UpdateApplication_WhenThereIsAlreadyApprovedWorkshop_ShouldRet var id = new Guid("08da8609-a211-4d74-82c0-dc89ea1fb2a7"); var entity = WithApplication(id); var changedEntity = WithApplication(id, ApplicationStatus.Approved); - var userId = Guid.NewGuid().ToString(); var workshop = WithWorkshop(new Guid("08da85ea-5b3c-4991-8416-2673d9421ca9")); applicationRepositoryMock.Setup(a => a.Update(It.IsAny(), It.IsAny>())) @@ -674,7 +686,6 @@ public async Task UpdateApplication_WhenThereIsAlreadyApprovedWorkshop_ShouldRet mapper.Setup(m => m.Map(It.IsAny())).Returns(new ApplicationDto() {Id = id, Status = ApplicationStatus.Approved}); - var expected = new ApplicationDto() {Id = id, Status = ApplicationStatus.Approved}; var update = new ApplicationUpdate { Id = id, @@ -704,7 +715,7 @@ public async Task UpdateApplication_WhenThereIsAlreadyApprovedWorkshop_ShouldRet workshopRepositoryMock.Setup(w => w.GetAvailableSeats(It.IsAny())).ReturnsAsync(uint.MaxValue); currentUserServiceMock.Setup(c => c.UserRole).Returns("provider"); - currentUserServiceMock.Setup(c => c.UserSubRole).Returns(""); + currentUserServiceMock.Setup(c => c.UserSubRole).Returns(string.Empty); applicationRepositoryMock.Setup(a => a.Count(It.IsAny>>())).ReturnsAsync(int.MaxValue); @@ -719,7 +730,6 @@ public async Task UpdateApplication_WhenThereIsNoAlreadyApprovedWorkshop_Succeed var id = new Guid("08da8609-a211-4d74-82c0-dc89ea1fb2a7"); var entity = WithApplication(id); var changedEntity = WithApplication(id, ApplicationStatus.Approved); - var userId = Guid.NewGuid().ToString(); var workshop = WithWorkshop(new Guid("08da85ea-5b3c-4991-8416-2673d9421ca9")); applicationRepositoryMock.Setup(a => a.Update(It.IsAny(), It.IsAny>())) @@ -734,7 +744,6 @@ public async Task UpdateApplication_WhenThereIsNoAlreadyApprovedWorkshop_Succeed mapper.Setup(m => m.Map(It.IsAny())).Returns(new ApplicationDto() {Id = id, Status = ApplicationStatus.Approved}); - var expected = new ApplicationDto() {Id = id, Status = ApplicationStatus.Approved}; var update = new ApplicationUpdate { Id = id, @@ -764,7 +773,7 @@ public async Task UpdateApplication_WhenThereIsNoAlreadyApprovedWorkshop_Succeed workshopRepositoryMock.Setup(w => w.GetAvailableSeats(It.IsAny())).ReturnsAsync(uint.MaxValue); currentUserServiceMock.Setup(c => c.UserRole).Returns("provider"); - currentUserServiceMock.Setup(c => c.UserSubRole).Returns(""); + currentUserServiceMock.Setup(c => c.UserSubRole).Returns(string.Empty); applicationRepositoryMock.Setup(a => a.Count(It.IsAny>>())).ReturnsAsync(int.MinValue); @@ -776,7 +785,6 @@ public async Task UpdateApplication_WhenThereIsNoAlreadyApprovedWorkshop_Succeed public void UpdateApplication_WhenModelIsNull_ShouldThrowArgumentException() { // Act and Assert - var userId = Guid.NewGuid().ToString(); service.Invoking(s => s.Update(null, Guid.NewGuid())).Should().ThrowAsync(); } @@ -814,7 +822,7 @@ private void SetupCreate(Application application) mapper.Setup(m => m.Map(It.IsAny())) .Returns(application); mapper.Setup(m => m.Map(It.IsAny())) - .Returns(new ApplicationDto() {Id = new Guid("1745d16a-6181-43d7-97d0-a1d6cc34a8bd")}); + .Returns(new ApplicationDto() {Id = new Guid("1745d16a-6181-43d7-97d0-a1d6cc34a8bd") }); } private void SetupGetAll(List apps) @@ -824,8 +832,8 @@ private void SetupGetAll(List apps) It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny>>(), - It.IsAny>,SortDirection>>(), + It.IsAny>>(), + It.IsAny>, SortDirection>>(), It.IsAny())) .Returns(new List { apps.First() }.AsTestAsyncEnumerableQuery()); mapper.Setup(m => m.Map>(It.IsAny>())).Returns(mappedDtos); @@ -861,7 +869,7 @@ private void SetupGetAllBy(IEnumerable apps) applicationRepositoryMock.Setup(a => a.GetByFilter( It.IsAny>>(), It.IsAny())) - .Returns(Task.FromResult>(new List {apps.First()})); + .Returns(Task.FromResult>(new List {apps.First() })); mapper.Setup(m => m.Map>(It.IsAny>())).Returns(mappedDtos); } @@ -871,7 +879,7 @@ private void SetupGetAllByWorkshop(List apps) var mappedDtos = apps.Select(a => new ApplicationDto() {Id = a.Id}).ToList(); currentUserServiceMock.Setup(c => c.IsAdmin()).Returns(false); - currentUserServiceMock.Setup(c => c.UserHasRights(new IUserRights[] {new ProviderRights(apps.First().Workshop.ProviderId)})) + currentUserServiceMock.Setup(c => c.UserHasRights(new IUserRights[] {new ProviderRights(apps.First().Workshop.ProviderId) })) .Verifiable(); applicationRepositoryMock.Setup(r => r.Get( @@ -1074,8 +1082,6 @@ private List WithApplicationsList() }; } - - private Application WithApplication(Guid id, ApplicationStatus status = ApplicationStatus.Pending) { return new Application() diff --git a/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatMessageWorkshopServiceTests.cs b/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatMessageWorkshopServiceTests.cs index 129d89caef..e33903b92e 100644 --- a/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatMessageWorkshopServiceTests.cs +++ b/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatMessageWorkshopServiceTests.cs @@ -112,6 +112,7 @@ public async Task Create_WhenMessageIsValid_ShouldSaveMessageInDatabase() #region GetMessagesForChatRoomAsync [Test] + [Ignore("Testing of unused method")] public void GetMessagesForChatRoomAsync_WhenOffsetfilterIsNull_ShouldNotThrowException() { // Arrange @@ -123,6 +124,7 @@ public void GetMessagesForChatRoomAsync_WhenOffsetfilterIsNull_ShouldNotThrowExc } [Test] + [Ignore("Testing of unused method")] public async Task GetMessagesForChatRoomAsync_WhenCalledWithAllValidParameters_ShouldReturnFoundMessages() { // Arrange @@ -141,6 +143,7 @@ public async Task GetMessagesForChatRoomAsync_WhenCalledWithAllValidParameters_S } [Test] + [Ignore("Testing of unused method")] public async Task GetMessagesForChatRoomAsync_WhenCalledWithUnexistedRoomId_ShouldReturnEmptyList() { // Arrange @@ -193,6 +196,19 @@ public async Task GetMessagesForChatRoomAndSetReadDateTimeIfItIsNullAsync_WhenCa Assert.AreEqual(existingChatRoomId, result.FirstOrDefault()?.ChatRoomId); }); } + + [Test] + public async Task GetMessagesForChatRoomAndSetReadDateTimeIfItIsNullAsync_WhenCalledWithInValidParameters_ShouldReturnEmptyList() + { + // Arrange + var invalidChatRoomId = Guid.NewGuid(); + + // Act + var result = await messageService.GetMessagesForChatRoomAndSetReadDateTimeIfItIsNullAsync(invalidChatRoomId, new OffsetFilter(), Role.Provider).ConfigureAwait(false); + + // Assert + Assert.IsEmpty(result); + } #endregion private void SeedDatabase() diff --git a/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatRoomWorkshopServiceTests.cs b/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatRoomWorkshopServiceTests.cs index 6dbb9254a5..4d290d901e 100644 --- a/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatRoomWorkshopServiceTests.cs +++ b/OutOfSchool/OutOfSchool.WebApi.Tests/Services/ChatRoomWorkshopServiceTests.cs @@ -278,6 +278,7 @@ public async Task GetByProviderIdAsync_WhenRoomDoesNotExist_ShouldReturnNull() #region GetByWorkshopIdAsync [Test] + [Ignore("Testing of unused method")] public async Task GetByWorkshopIdAsync_WhenRoomExist_ShouldReturnFoundEntity() { // Arrange @@ -298,6 +299,7 @@ public async Task GetByWorkshopIdAsync_WhenRoomExist_ShouldReturnFoundEntity() } [Test] + [Ignore("Testing of unused method")] public async Task GetByWorkshopIdAsync_WhenRoomDoesNotExist_ShouldReturnNull() { // Arrange @@ -380,6 +382,38 @@ public async Task GetChatRoomIdsByProviderIdAsync_WhenRoomDoesNotExist_ShouldRet } #endregion + #region GetChatRoomIdsByWorkshopIdsAsync + [Test] + public async Task GetChatRoomIdsByWorkshopIdsAsync_WhenRoomExist_ShouldReturnFoundEntity() + { + // Arrange + var existingworkshopIds = workshops.Select(x => x.Id); + + // Act + var result = await roomService.GetChatRoomIdsByWorkshopIdsAsync(existingworkshopIds).ConfigureAwait(false); + + // Assert + Assert.IsInstanceOf>(result); + Assert.IsNotNull(result); + Assert.IsTrue(result.Any()); + } + + [Test] + public async Task GetChatRoomIdsByWorkshopIdsAsync_WhenRoomDoesNotExist_ShouldReturnNull() + { + // Arrange + var notExistingWorkshopIds = new Guid[] { Guid.NewGuid(), Guid.NewGuid() }; + + // Act + var result = await roomService.GetChatRoomIdsByWorkshopIdsAsync(notExistingWorkshopIds).ConfigureAwait(false); + + // Assert + Assert.IsInstanceOf>(result); + Assert.IsNotNull(result); + Assert.IsFalse(result.Any()); + } + #endregion + #region GetUniqueChatRoomAsync [Test] public async Task GetUniqueChatRoom_WhenRoomExists_ShouldReturnFoundEntity() diff --git a/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/ChatWorkshopController.cs b/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/ChatWorkshopController.cs index f8d3bd32c1..3f3dadbd66 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/ChatWorkshopController.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/ChatWorkshopController.cs @@ -1,9 +1,12 @@ -using System.Security.Authentication; +using System; +using System.Security.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Localization; +using Nest; using OutOfSchool.Services.Enums; using OutOfSchool.WebApi.Common; using OutOfSchool.WebApi.Models; +using OutOfSchool.WebApi.Models.Application; using OutOfSchool.WebApi.Models.ChatWorkshop; namespace OutOfSchool.WebApi.Controllers.V1; @@ -14,7 +17,7 @@ namespace OutOfSchool.WebApi.Controllers.V1; [ApiController] [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] -[Authorize(Roles = "provider,parent,ministryadmin")] +[Authorize(Roles = "provider,parent")] public class ChatWorkshopController : ControllerBase { // TODO: define the algorithm of logging information and warnings in the solution @@ -25,9 +28,7 @@ public class ChatWorkshopController : ControllerBase private readonly IStringLocalizer localizer; private readonly ILogger logger; private readonly IProviderAdminService providerAdminService; - - // TODO: figure out why we have ministry admin here :) - private readonly IMinistryAdminService ministryAdminService; + private readonly IApplicationService applicationService; /// /// Initializes a new instance of the class. @@ -38,7 +39,7 @@ public class ChatWorkshopController : ControllerBase /// Localizer. /// Logger. /// Service for Provider's admins. - /// Service for Ministry admins. + /// Service for Applications. public ChatWorkshopController( IChatMessageWorkshopService messageService, IChatRoomWorkshopService roomService, @@ -46,7 +47,7 @@ public ChatWorkshopController( IStringLocalizer localizer, ILogger logger, IProviderAdminService providerAdminService, - IMinistryAdminService ministryAdminService) + IApplicationService applicationService) { this.messageService = messageService ?? throw new ArgumentNullException(nameof(messageService)); this.roomService = roomService ?? throw new ArgumentNullException(nameof(roomService)); @@ -54,7 +55,7 @@ public ChatWorkshopController( this.localizer = localizer ?? throw new ArgumentNullException(nameof(localizer)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); this.providerAdminService = providerAdminService ?? throw new ArgumentNullException(nameof(providerAdminService)); - this.ministryAdminService = ministryAdminService ?? throw new ArgumentNullException(nameof(ministryAdminService)); + this.applicationService = applicationService ?? throw new ArgumentNullException(nameof(applicationService)); } /// @@ -72,6 +73,21 @@ public ChatWorkshopController( public Task GetRoomForParentByRoomIdAsync(Guid id) => this.GetRoomByIdAsync(id, this.IsParentAChatRoomParticipantAsync); + /// + /// Get existing or create new chat room. + /// + /// Application Id. + /// Existiong or new chat room. + [HttpGet("chatrooms/applications/{applicationId}")] + [Authorize(Roles = "parent,provider")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ChatRoomWorkshopDto))] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public Task GetRoomByApplicationIdAsync(Guid applicationId) + => this.GetOrCreateRoomByApplicationIdAsync(applicationId); + /// /// Get provider's chat room with information about Parent and Workshop. /// @@ -94,6 +110,7 @@ public Task GetRoomForProviderByRoomIdAsync(Guid id) /// Workshop's Id. /// Filter to get specified portion of messages in the chat room. /// User's chat room's messages that were found. + [Obsolete("Become unused")] [HttpGet("parent/workshops/{id}/messages")] [Authorize(Roles = "parent")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(List))] @@ -161,22 +178,6 @@ public Task GetMessagesForParentByRoomIdAsync(Guid id, [FromQuery public Task GetMessagesForProviderByRoomIdAsync(Guid id, [FromQuery] OffsetFilter offsetFilter) => this.GetMessagesByRoomIdAsync(id, offsetFilter, this.IsProviderAChatRoomParticipantAsync); - /// - /// Gets a portion of chat messages for Ministry Admin if it has related provider participants by specified chat room id. - /// - /// ChatRoom's Id. - /// Filter to get specified portion of messages in the chat room. - /// User's chat room's messages that were found. - [HttpGet("ministryadmin/chatrooms/{id}/messages")] - [Authorize(Roles = "ministryadmin")] - [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(List))] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public Task GetMessagesForMinistryAdminByRoomIdAsync(Guid id, [FromQuery] OffsetFilter offsetFilter) - => this.GetMessagesByRoomIdAsync(id, offsetFilter, IsMinistryAdminAbleToBeSeeChatRoomMessagesAsync); - /// /// Get a list of chat rooms for current parent. /// @@ -188,7 +189,7 @@ public Task GetMessagesForMinistryAdminByRoomIdAsync(Guid id, [Fr [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public Task GetParentsRoomsAsync([FromQuery]ChatWorkshopFilter filter = null) + public Task GetParentsRoomsAsync([FromQuery] ChatWorkshopFilter filter = null) => this.GetUsersRoomsAsync(parentId => roomService.GetByParentIdAsync(parentId), filter); /// @@ -210,6 +211,22 @@ public Task GetProvidersRoomsAsync([FromQuery] ChatWorkshopFilter /// /// WorkShop's Id. /// ChatRoom that was found. + [HttpGet("parent/chatrooms/workshop/{workshopId}")] + [Authorize(Roles = "parent")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ChatRoomWorkshopDto))] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public Task GetParentsRoomByWorkshopIdAsync(Guid workshopId) + => GetParentRoomByWorkshopIdAsync(workshopId); + + /// + /// Get a chat room for current parent and workshopId. + /// + /// WorkShop's Id. + /// ChatRoom that was found. + [Obsolete("Was not used")] [HttpGet("parent/chatroomforworkshop/{workshopId}")] [Authorize(Roles = "parent")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ChatRoomWorkshopDto))] @@ -225,6 +242,7 @@ public Task GetParentsRoomByWorkshopAsync(Guid workshopId) /// /// WorkShop's Id. /// ChatRoom that was found. + [Obsolete("Was not used")] [HttpGet("parent/chatroomwithmessagesforworkshop/{workshopId}")] [Authorize(Roles = "parent")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ChatRoomWorkshopDtoWithLastMessage))] @@ -240,6 +258,7 @@ public Task GetParentsRoomWithMessagesByWorkshopAsync(Guid worksh /// /// Parent's Id. /// ChatRoom that was found. + [Obsolete("Was not used")] [HttpGet("provider/chatroomsforparent/{parentId}")] [Authorize(Roles = "provider")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable))] @@ -255,6 +274,7 @@ public Task GetProvidersRoomsByParentAsync(Guid parentId) /// /// Parent's Id. /// ChatRoom that was found. + [Obsolete("Was not used")] [HttpGet("provider/chatroomswithmessagesforparent/{parentId}")] [Authorize(Roles = "provider")] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable))] @@ -265,37 +285,55 @@ public Task GetProvidersRoomsByParentAsync(Guid parentId) public Task GetProvidersRoomsWithMessagesByParentAsync(Guid parentId) => GetProvidersRoomsByParentIdAsync(parentId, true); - /// - /// Get a list of chat rooms for the ministry admin by specific provider id. - /// - /// Provider Id. - /// List of ChatRooms with last message and number of not read messages. - [HttpGet("ministryadmin/chatrooms")] - [Authorize(Roles = "ministryadmin")] - [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable))] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task GetRoomsForMinistryAdminByProviderIdAsync([FromQuery] Guid providerId) + private async Task GetOrCreateRoomByApplicationIdAsync(Guid applicationId) { async Task Operation() { - var userId = GettingUserProperties.GetUserId(HttpContext); + logger.LogInformation($"User {GettingUserProperties.GetUserId(HttpContext)} is trying to get chat room by {nameof(applicationId)} = {applicationId}"); + var application = await applicationService.GetById(applicationId); + + if (application is null) + { + return NoContent(); + } - var isProviderSubordinate = await ministryAdminService.IsProviderSubordinateAsync(userId, providerId); - if (!isProviderSubordinate) + var userHasRights = await IsUserHasRightsForApplicationAsync(application); + + if (!userHasRights) + { + logger.LogWarning($"User {GettingUserProperties.GetUserId(HttpContext)} is trying to get chat room by {nameof(applicationId)} = {applicationId} with no rights"); + return BadRequest(); + } + + var chatroom = await roomService.CreateOrReturnExistingAsync(application.WorkshopId, application.ParentId); + + if (chatroom is null) { - return Forbid(); + logger.LogWarning($"User {GettingUserProperties.GetUserId(HttpContext)} is trying to get chat room by {nameof(applicationId)} = {applicationId} but get null"); + return BadRequest(); } - var chatRooms = await roomService.GetByProviderIdAsync(providerId); - return chatRooms.Any() ? Ok(chatRooms) : NoContent(); + return Ok(chatroom); } return await HandleOperationAsync(Operation); } + private async Task IsUserHasRightsForApplicationAsync(ApplicationDto application) + { + var userId = GettingUserProperties.GetUserId(HttpContext); + var userRole = GettingUserProperties.GetUserRole(HttpContext); + + if (userRole == Role.Parent) + { + return await validationService.UserIsParentOwnerAsync(userId, application.ParentId); + } + + var userSubrole = GettingUserProperties.GetUserSubrole(HttpContext); + + return await validationService.UserIsWorkshopOwnerAsync(userId, application.WorkshopId, userSubrole); + } + private async Task IsParentAChatRoomParticipantAsync(ChatRoomWorkshopDto chatRoom) { var userId = GettingUserProperties.GetUserId(HttpContext); @@ -325,15 +363,6 @@ private async Task IsProviderAChatRoomParticipantAsync(ChatRoomWorkshopDto return result; } - private async Task IsMinistryAdminAbleToBeSeeChatRoomMessagesAsync(ChatRoomWorkshopDto chatRoom) - { - var userId = GettingUserProperties.GetUserId(HttpContext); - - return await ministryAdminService.IsProviderSubordinateAsync( - userId, - chatRoom.Workshop?.ProviderId ?? throw new InvalidOperationException($"Unable to retrieve workshop data from chatRoom with id = {chatRoom.Id}")); - } - private void LogWarningAboutUsersTryingToGetNotOwnChatRoom(Guid chatRoomId, string userId) { var messageToLog = $"User with {nameof(userId)}:{userId} is trying to get not his own chat room: {nameof(chatRoomId)}={chatRoomId}."; @@ -396,6 +425,38 @@ async Task Operation() return await HandleOperationAsync(Operation); } + private async Task GetParentRoomByWorkshopIdAsync(Guid workshopId) + { + async Task Operation() + { + logger.LogInformation($"User {GettingUserProperties.GetUserId(HttpContext)} is trying to get chat room by {nameof(workshopId)} = {workshopId}"); + var userId = GettingUserProperties.GetUserId(HttpContext); + var userRole = Role.Parent; + + var parentId = await validationService.GetParentOrProviderIdByUserRoleAsync(userId, userRole).ConfigureAwait(false); + + if (parentId != Guid.Empty) + { + logger.LogWarning($"User {userId} is trying to get chat room but has no rights"); + + var chatRoom = await roomService.CreateOrReturnExistingAsync(workshopId, parentId).ConfigureAwait(false); + + if (chatRoom is not null) + { + return Ok(chatRoom); + } + } + + logger.LogWarning($"User {userId} is trying to get chat room by {nameof(workshopId)} = {workshopId} but get null"); + + return BadRequest(); + + } + + return await HandleOperationAsync(Operation); + } + + [Obsolete("Was not used")] private async Task GetParentRoomByWorkshopIdAsync(Guid workshopId, bool withMessages) { async Task Operation() @@ -423,6 +484,7 @@ async Task Operation() return await HandleOperationAsync(Operation); } + [Obsolete("Was not used")] private async Task GetProvidersRoomsByParentIdAsync(Guid parentId, bool withMessages) { async Task Operation() diff --git a/OutOfSchool/OutOfSchool.WebApi/Hubs/ChatWorkshopHub.cs b/OutOfSchool/OutOfSchool.WebApi/Hubs/ChatWorkshopHub.cs index 421d4c181a..697b4d188b 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Hubs/ChatWorkshopHub.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Hubs/ChatWorkshopHub.cs @@ -130,6 +130,17 @@ public async Task SendMessageToOthersInGroupAsync(string chatNewMessage) // Deserialize from string to Object var chatMessageWorkshopCreateDto = JsonConvert.DeserializeObject(chatNewMessage); + var chatRoomExists = await roomService.GetByIdAsync(chatMessageWorkshopCreateDto.ChatRoomId).ConfigureAwait(false) is not null; + + if (!chatRoomExists) + { + logger.LogWarning($"{GettingUserProperties.GetUserRole(Context.User)} with UserId:{GettingUserProperties.GetUserId(Context.User)} is trying to send message to not existing chatRoom"); + + var messageForUser = localizer["Some of the message parameters were wrong. Please check your message and try again."]; + await Clients.Caller.SendAsync("ReceiveMessageInChatGroup", messageForUser).ConfigureAwait(false); + return; + } + var userHasRights = await this.UserHasRigtsForChatRoomAsync(chatMessageWorkshopCreateDto.WorkshopId, chatMessageWorkshopCreateDto.ParentId).ConfigureAwait(false); if (!userHasRights) diff --git a/OutOfSchool/OutOfSchool.WebApi/Models/ChatWorkshop/ChatMessageWorkshopCreateDto.cs b/OutOfSchool/OutOfSchool.WebApi/Models/ChatWorkshop/ChatMessageWorkshopCreateDto.cs index d65e5ceb2f..3218b9f16c 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Models/ChatWorkshop/ChatMessageWorkshopCreateDto.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Models/ChatWorkshop/ChatMessageWorkshopCreateDto.cs @@ -14,6 +14,10 @@ public class ChatMessageWorkshopCreateDto [JsonProperty("ParentId", Required = Required.Always)] public Guid ParentId { get; set; } + [Required] + [JsonProperty("ChatRoomId", Required = Required.Always)] + public Guid ChatRoomId { get; set; } + [Required] [MaxLength(Constants.ChatMessageTextMaxLength)] [JsonProperty("Text", Required = Required.Always)] diff --git a/OutOfSchool/OutOfSchool.WebApi/Services/ApplicationService.cs b/OutOfSchool/OutOfSchool.WebApi/Services/ApplicationService.cs index d50419fb88..cf5f314a2b 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Services/ApplicationService.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Services/ApplicationService.cs @@ -408,6 +408,7 @@ public async Task GetById(Guid id) await currentUserService.UserHasRights( new ParentRights(application.ParentId), + new ProviderRights(application.Workshop.ProviderId), new ProviderAdminWorkshopRights(application.Workshop.ProviderId, application.Workshop.Id)); return mapper.Map(application); diff --git a/OutOfSchool/OutOfSchool.WebApi/Services/ChatMessageWorkshopService.cs b/OutOfSchool/OutOfSchool.WebApi/Services/ChatMessageWorkshopService.cs index 688ed8365a..a56b37087c 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Services/ChatMessageWorkshopService.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Services/ChatMessageWorkshopService.cs @@ -55,9 +55,6 @@ public async Task CreateAsync(ChatMessageWorkshopCreateD { var userRoleIsProvider = userRole != Role.Parent; - // find or create new chat room and then set it's Id to the Message model - var chatRoomDto = await roomService.CreateOrReturnExistingAsync(chatMessageCreateDto.WorkshopId, chatMessageCreateDto.ParentId).ConfigureAwait(false); - // create new dto object that will be saved to the database var chatMessageDtoThatWillBeSaved = new ChatMessageWorkshop() { @@ -65,13 +62,18 @@ public async Task CreateAsync(ChatMessageWorkshopCreateD Text = chatMessageCreateDto.Text, CreatedDateTime = DateTimeOffset.UtcNow, ReadDateTime = null, - ChatRoomId = chatRoomDto.Id, + ChatRoomId = chatMessageCreateDto.ChatRoomId, }; var chatMessage = await messageRepository.Create(chatMessageDtoThatWillBeSaved).ConfigureAwait(false); logger.LogDebug($"{nameof(ChatMessageWorkshop)} id:{chatMessage.Id} was saved to DB."); return mapper.Map(chatMessage); } + catch (ArgumentNullException exception) + { + logger.LogError($"{nameof(ChatRoomWorkshopDto)} not exist. Exception: {exception.Message}"); + throw; + } catch (DbUpdateException exception) { logger.LogError($"{nameof(ChatMessageWorkshop)} was not created. Exception: {exception.Message}"); @@ -80,6 +82,7 @@ public async Task CreateAsync(ChatMessageWorkshopCreateD } /// + [Obsolete("Unused")] public async Task> GetMessagesForChatRoomAsync(Guid chatRoomId, OffsetFilter offsetFilter) { try diff --git a/OutOfSchool/OutOfSchool.WebApi/Services/ChatRoomWorkshopService.cs b/OutOfSchool/OutOfSchool.WebApi/Services/ChatRoomWorkshopService.cs index 589ca0eba8..5a4ce6237e 100644 --- a/OutOfSchool/OutOfSchool.WebApi/Services/ChatRoomWorkshopService.cs +++ b/OutOfSchool/OutOfSchool.WebApi/Services/ChatRoomWorkshopService.cs @@ -49,7 +49,8 @@ public async Task CreateOrReturnExistingAsync(Guid workshop { var newChatRoom = await this.CreateAsync(workshopId, parentId).ConfigureAwait(false); logger.LogDebug($"{nameof(ChatRoomWorkshop)} id:{newChatRoom.Id} was saved to DB."); - return mapper.Map(newChatRoom); + var chatRoomDto = await roomRepository.GetById(newChatRoom.Id).ConfigureAwait(false); + return mapper.Map(chatRoomDto); } else { @@ -120,6 +121,7 @@ public async Task GetByIdAsync(Guid id) } /// + [Obsolete("Was not used")] public async Task> GetByParentIdProviderIdAsync(Guid parentId, Guid providerId) { logger.LogDebug("Process of getting ChatRooms with parentId:{parentId} and providerId:{providerId} was started.", parentId, providerId); @@ -154,6 +156,7 @@ public async Task> GetByParentIdProviderIdAsync } /// + [Obsolete("Was not used")] public async Task> GetWithMessagesByParentIdProviderIdAsync(Guid parentId, Guid providerId) { logger.LogDebug("Process of getting ChatRoomWorkshopDtoWithLastMessage with parentId:{parentId} and providerId:{providerId} was started.", parentId, providerId); @@ -187,6 +190,7 @@ public async Task> GetWithMessag } /// + [Obsolete("Become unused")] public async Task GetByParentIdWorkshopIdAsync(Guid parentId, Guid workshopId) { logger.LogDebug("Process of getting ChatRoom with parentId:{parentId} and workshopId:{workshopId} was started.", parentId, workshopId); @@ -221,6 +225,7 @@ public async Task GetByParentIdWorkshopIdAsync(Guid parentI } /// + [Obsolete("Was not used")] public async Task GetWithMessagesByParentIdWorkshopIdAsync(Guid parentId, Guid workshopId) { logger.LogDebug( @@ -297,6 +302,7 @@ public async Task> GetByProvider } /// + [Obsolete("Unused")] public async Task> GetByWorkshopIdAsync(Guid workshopId) { logger.LogDebug($"Process of getting {nameof(ChatRoomWorkshopDtoWithLastMessage)}(s/es) with {nameof(workshopId)}:{workshopId} was started."); @@ -317,6 +323,7 @@ public async Task> GetByWorkshop } /// + [Obsolete("Unused")] public async Task> GetByWorkshopIdsAsync(IEnumerable workshopIds) { string workshopIdsStr = $"{nameof(workshopIds)}:{string.Join(", ", workshopIds)}";