diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/ParticipantServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/ParticipantServiceTests.cs index 80057add..8537a802 100644 --- a/Server/ReasnAPI/ReasnAPI.Tests/Services/ParticipantServiceTests.cs +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/ParticipantServiceTests.cs @@ -11,62 +11,6 @@ namespace ReasnAPI.Tests.Services; [TestClass] public class ParticipantServiceTests { - [TestMethod] - public void GetParticipantById_ParticipantExists_ParticipantReturned() - { - var mockContext = new Mock(); - - var event1 = new Event - { - Id = 1, - Name = "Event", - Description = "Description" - }; - - var user = new User - { - Id = 1, - Username = "User", - Email = "Email", - Password = "Password" - }; - - var participant = new Participant - { - Id = 1, - EventId = event1.Id, - UserId = user.Id, - Status = ParticipantStatus.Interested - }; - - var fakeParticipant = new FakeDbSet { participant }; - var fakeEvent = new FakeDbSet { event1 }; - var fakeUser = new FakeDbSet { user }; - - mockContext.Setup(c => c.Events).Returns(fakeEvent); - mockContext.Setup(c => c.Users).Returns(fakeUser); - mockContext.Setup(c => c.Participants).Returns(fakeParticipant); - - var participantService = new ParticipantService(mockContext.Object); - - var result = participantService.GetParticipantById(1); - - Assert.IsNotNull(result); - Assert.AreEqual(1, result.EventId); - Assert.AreEqual(1, result.UserId); - Assert.AreEqual(ParticipantStatus.Interested, result.Status); - } - - [TestMethod] - public void GetParticipantById_ParticipantDoesNotExist_NullReturned() - { - var mockContext = new Mock(); - mockContext.Setup(c => c.Participants).ReturnsDbSet([]); - - var participantService = new ParticipantService(mockContext.Object); - - Assert.ThrowsException(() => participantService.GetParticipantById(1)); - } [TestMethod] public void GetAllParticipants_ParticipantsExist_ParticipantsReturned() @@ -99,16 +43,16 @@ public void GetAllParticipants_ParticipantsExist_ParticipantsReturned() var participant1 = new Participant { Id = 1, - EventId = event1.Id, - UserId = user1.Id, + Event = event1, + User = user1, Status = ParticipantStatus.Interested }; var participant2 = new Participant { Id = 2, - EventId = event1.Id, - UserId = user2.Id, + Event = event1, + User = user2, Status = ParticipantStatus.Participating }; @@ -169,16 +113,18 @@ public void GetParticipantsByFilter_ParticipantsExist_ParticipantsReturned() var participant1 = new Participant { Id = 1, + Event = event1, EventId = event1.Id, - UserId = user1.Id, + User = user1, Status = ParticipantStatus.Interested }; var participant2 = new Participant { Id = 2, + Event = event1, EventId = event1.Id, - UserId = user2.Id, + User = user2, Status = ParticipantStatus.Participating }; @@ -217,13 +163,14 @@ public void CreateParticipant_ParticipantCreated_ParticipantReturned() { Id = 1, Name = "Event", - Description = "Description" + Description = "Description", + Slug = "Event-Slug" }; var user = new User { Id = 1, - Username = "User", + Username = "Username", Email = "Email", Password = "Password" }; @@ -236,16 +183,16 @@ public void CreateParticipant_ParticipantCreated_ParticipantReturned() var participantDto = new ParticipantDto { - EventId = 1, - UserId = 1, + EventSlug = "Event-Slug", + Username = "Username", Status = ParticipantStatus.Interested }; - var result = participantService.CreateParticipant(participantDto); + var result = participantService.CreateOrUpdateParticipant(participantDto); Assert.IsNotNull(result); - Assert.AreEqual(1, result.EventId); - Assert.AreEqual(1, result.UserId); + Assert.AreEqual("Event-Slug", result.EventSlug); + Assert.AreEqual("Username", result.Username); Assert.AreEqual(ParticipantStatus.Interested, result.Status); } @@ -257,7 +204,7 @@ public void CreateParticipant_ParticipantDtoIsNull_NullReturned() var participantService = new ParticipantService(mockContext.Object); - Assert.ThrowsException(() => participantService.CreateParticipant(null)); + Assert.ThrowsException(() => participantService.CreateOrUpdateParticipant(null)); } [TestMethod] @@ -269,13 +216,14 @@ public void UpdateParticipant_ParticipantExists_ParticipantReturned() { Id = 1, Name = "Event", - Description = "Description" + Description = "Description", + Slug = "Event-Slug" }; var user = new User { Id = 1, - Username = "User", + Username = "Username", Email = "Email", Password = "Password" }; @@ -283,8 +231,8 @@ public void UpdateParticipant_ParticipantExists_ParticipantReturned() var participant = new Participant { Id = 1, - EventId = event1.Id, - UserId = user.Id, + Event = event1, + User = user, Status = ParticipantStatus.Interested }; @@ -294,16 +242,16 @@ public void UpdateParticipant_ParticipantExists_ParticipantReturned() var participantService = new ParticipantService(mockContext.Object); - var result = participantService.UpdateParticipant(1, new ParticipantDto + var result = participantService.CreateOrUpdateParticipant(new ParticipantDto { - EventId = 2, - UserId = 2, + EventSlug = "Event-Slug", + Username = "Username", Status = ParticipantStatus.Participating }); Assert.IsNotNull(result); - Assert.AreEqual(1, result.EventId); - Assert.AreEqual(1, result.UserId); + Assert.AreEqual("Event-Slug", result.EventSlug); + Assert.AreEqual("Username", result.Username); Assert.AreEqual(ParticipantStatus.Participating, result.Status); } @@ -311,14 +259,16 @@ public void UpdateParticipant_ParticipantExists_ParticipantReturned() public void UpdateParticipant_ParticipantDoesNotExist_NullReturned() { var mockContext = new Mock(); + mockContext.Setup(c => c.Events).ReturnsDbSet([]); + mockContext.Setup(c => c.Users).ReturnsDbSet([]); mockContext.Setup(c => c.Participants).ReturnsDbSet([]); var participantService = new ParticipantService(mockContext.Object); - Assert.ThrowsException(() => participantService.UpdateParticipant(1, new ParticipantDto + Assert.ThrowsException(() => participantService.CreateOrUpdateParticipant(new ParticipantDto { - EventId = 2, - UserId = 2, + EventSlug = "Event-Slug", + Username = "Username", Status = ParticipantStatus.Participating })); } @@ -333,20 +283,45 @@ public void UpdateParticipant_ParticipantDtoIsNull_NullReturned() var participantService = new ParticipantService(mockContext.Object); - Assert.ThrowsException(() => participantService.UpdateParticipant(1, null)); + Assert.ThrowsException(() => participantService.CreateOrUpdateParticipant(null)); } [TestMethod] public void DeleteParticipant_ParticipantExists_ParticipantDeleted() { var mockContext = new Mock(); - mockContext.Setup(c => c.Participants).ReturnsDbSet([ - new() { Id = 1, EventId = 1, UserId = 1, Status = ParticipantStatus.Participating } - ]); + + var event1 = new Event + { + Id = 1, + Name = "Event", + Description = "Description", + Slug = "Event-Slug" + }; + + var user = new User + { + Id = 1, + Username = "Username", + Email = "Email", + Password = "Password" + }; + + var participant = new Participant + { + Id = 1, + Event = event1, + User = user, + Status = ParticipantStatus.Participating + }; + + mockContext.Setup(c => c.Participants).ReturnsDbSet([participant]); + mockContext.Setup(c => c.Events).ReturnsDbSet([event1]); + mockContext.Setup(c => c.Users).ReturnsDbSet([user]); var participantService = new ParticipantService(mockContext.Object); - participantService.DeleteParticipant(1); + participantService.DeleteParticipant(user.Id, event1.Slug); mockContext.Verify(c => c.SaveChanges(), Times.Once); } @@ -356,10 +331,12 @@ public void DeleteParticipant_ParticipantDoesNotExist_NothingDeleted() { var mockContext = new Mock(); mockContext.Setup(c => c.Participants).ReturnsDbSet([]); + mockContext.Setup(c => c.Events).ReturnsDbSet([]); + mockContext.Setup(c => c.Users).ReturnsDbSet([]); var participantService = new ParticipantService(mockContext.Object); - Assert.ThrowsException(() => participantService.DeleteParticipant(1)); + Assert.ThrowsException(() => participantService.DeleteParticipant(1, "Event-Slug")); mockContext.Verify(c => c.SaveChanges(), Times.Never); } } \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/UserServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/UserServiceTests.cs index a7758b7e..9638d623 100644 --- a/Server/ReasnAPI/ReasnAPI.Tests/Services/UserServiceTests.cs +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/UserServiceTests.cs @@ -27,16 +27,28 @@ public void GetUserById_UserExists_UserReturned() { var mockContext = new Mock(); + var address = new Address + { + Id = 1, + City = "City", + Country = "Country", + Street = "Street", + State = "State", + ZipCode = "ZipCode" + }; + var user = new User { Id = 1, Name = "John", Surname = "Doe", Username = "Username", - Email = "Email" + Email = "Email", + Address = address, }; mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + mockContext.Setup(c => c.Addresses).ReturnsDbSet([address]); var userService = new UserService(mockContext.Object); @@ -65,13 +77,24 @@ public void GetUserByUsername_UserExists_UserReturned() { var mockContext = new Mock(); + var address = new Address + { + Id = 1, + City = "City", + Country = "Country", + Street = "Street", + State = "State", + ZipCode = "ZipCode" + }; + var user = new User { Id = 1, Name = "John", Surname = "Doe", Username = "Username", - Email = "Email" + Email = "Email", + Address = address, }; mockContext.Setup(c => c.Users).ReturnsDbSet([user]); @@ -143,6 +166,7 @@ public void UpdateUser_UserUpdated_UserReturned() mockContext.Setup(c => c.Addresses).ReturnsDbSet([address]); mockContext.Setup(c => c.Users).ReturnsDbSet([user]); + mockContext.Setup(c => c.UserInterests).ReturnsDbSet([]); var userService = new UserService(mockContext.Object); @@ -157,7 +181,7 @@ public void UpdateUser_UserUpdated_UserReturned() Role = UserRole.User }; - var result = userService.UpdateUser(1, userDto); + var result = userService.UpdateUser("Username", userDto); Assert.IsNotNull(result); Assert.AreEqual("Jane", result.Name); @@ -187,7 +211,7 @@ public void UpdateUser_UserDtoIsNull_NullReturned() var userService = new UserService(mockContext.Object); - Assert.ThrowsException(() => userService.UpdateUser(1, null)); + Assert.ThrowsException(() => userService.UpdateUser("Username", null)); } [TestMethod] @@ -209,6 +233,6 @@ public void UpdateUser_UserDoesNotExist_NullReturned() AddressId = 1 }; - Assert.ThrowsException(() => userService.UpdateUser(1, userDto)); + Assert.ThrowsException(() => userService.UpdateUser("Username", userDto)); } } \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Controllers/MeController.cs b/Server/ReasnAPI/ReasnAPI/Controllers/MeController.cs index 825b1860..66ef6037 100644 --- a/Server/ReasnAPI/ReasnAPI/Controllers/MeController.cs +++ b/Server/ReasnAPI/ReasnAPI/Controllers/MeController.cs @@ -1,7 +1,8 @@ +using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using ReasnAPI.Mappers; using ReasnAPI.Models.DTOs; +using ReasnAPI.Models.Enums; using ReasnAPI.Services; namespace ReasnAPI.Controllers; @@ -9,90 +10,176 @@ namespace ReasnAPI.Controllers; [ApiController] [Authorize] [Route("[controller]")] -public class MeController : ControllerBase +public class MeController(UserService userService, EventService eventService, ParticipantService participantService, ImageService imageService) : ControllerBase { - private readonly UserService _userService; - - public MeController(UserService userService) - { - _userService = userService; - } + private readonly UserService _userService = userService; + private readonly EventService _eventService = eventService; + private readonly ParticipantService _participantService = participantService; + private readonly ImageService _imageService = imageService; [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] public IActionResult GetCurrentUser() { - var user = _userService.GetCurrentUser(); - return Ok(user.ToDto()); + var username = _userService.GetCurrentUser().Username; + var user = _userService.GetUserByUsername(username); + + return Ok(user); } [HttpPut] - public IActionResult UpdateCurrentUser() + [ProducesResponseType(StatusCodes.Status200OK)] + public IActionResult UpdateCurrentUser( + [FromBody] UserDto userDto, + [FromServices] IValidator validator) { - throw new NotImplementedException(); + validator.ValidateAndThrow(userDto); + + var user = _userService.GetCurrentUser(); + + // Users cant change their role in this endpoint + if (user.Role != userDto.Role) + { + return Forbid(); + } + + var updatedUser = _userService.UpdateUser(user.Username, userDto); + + return Ok(updatedUser); } - [HttpPost] + [HttpGet] [Route("image")] - public IActionResult AddCurrentUserImage() - { - throw new NotImplementedException(); + [ProducesResponseType(StatusCodes.Status200OK)] + public IActionResult GetCurrentUserImage() + { + var user = _userService.GetCurrentUser(); + var image = _imageService.GetImageByUserId(user.Id); + + if (image is null) + { + return NotFound(); + } + + return File(image.ImageData, "image/jpeg"); } - [HttpPut] + [HttpPost] [Route("image")] - public IActionResult UpdateCurrentUserImage() + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task AddCurrentUserImage([FromForm] List images) { - throw new NotImplementedException(); + var userId = _userService.GetCurrentUser().Id; + + foreach (var image in images.Where(image => image.Length > 0)) + { + using var ms = new MemoryStream(); + await image.CopyToAsync(ms); + var fileBytes = ms.ToArray(); + + var imageDto = new ImageDto + { + ObjectId = userId, + ObjectType = ObjectType.User, + ImageData = fileBytes + }; + + _imageService.CreateImages([imageDto]); + } + + return Ok(); } - [HttpDelete] + [HttpPut] [Route("image")] - public IActionResult DeleteCurrentUserImage() + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task UpdateCurrentUserImage([FromForm] List images) { - throw new NotImplementedException(); - } + var userId = _userService.GetCurrentUser().Id; - [HttpGet] - [Route("interests")] - public IActionResult GetCurrentUserInterests() - { - throw new NotImplementedException(); - } + foreach (var image in images.Where(image => image.Length > 0)) + { + using var ms = new MemoryStream(); + await image.CopyToAsync(ms); + var fileBytes = ms.ToArray(); - [HttpPost] - [Route("interests")] - public IActionResult AddCurrentUserInterest() - { - throw new NotImplementedException(); + var imageDto = new ImageDto + { + ObjectId = userId, + ObjectType = ObjectType.User, + ImageData = fileBytes + }; + + _imageService.UpdateImageForUser(userId, imageDto); + } + + return Ok(); } [HttpDelete] - [Route("interests/{interestId:int}")] - public IActionResult DeleteCurrentUserInterest(int interestId) + [Route("image")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public IActionResult DeleteCurrentUserImage() { - throw new NotImplementedException(); + var userId = _userService.GetCurrentUser().Id; + _imageService.DeleteImageByObjectIdAndType(userId, ObjectType.User); + + return NoContent(); } [HttpGet] [Route("events")] + [ProducesResponseType>(StatusCodes.Status200OK)] public IActionResult GetCurrentUserEvents() { - throw new NotImplementedException(); + var user = _userService.GetCurrentUser(); + var events = _eventService.GetUserEvents(user.Username); + + if (user.Role == UserRole.Organizer) + { + var organizerEvents = _eventService.GetEventsByFilter(e => e.OrganizerId == user.Id); + events = events.Concat(organizerEvents); + } + + return Ok(events); } [HttpPost] [Route("events/{slug}/enroll")] - public IActionResult EnrollCurrentUserInEvent(string slug) + [ProducesResponseType(StatusCodes.Status201Created)] + public IActionResult EnrollCurrentUserInEvent([FromRoute] string slug) { - throw new NotImplementedException(); + var user = _userService.GetCurrentUser(); + + var participant = _participantService.CreateOrUpdateParticipant(new ParticipantDto { EventSlug = slug, Username = user.Username, Status = ParticipantStatus.Interested }); + + var location = Url.Action( + action: nameof(GetCurrentUserEvents), + controller: "Me"); + + return Created(location, participant); } [HttpPost] [Route("events/{slug}/confirm")] - public IActionResult ConfirmCurrentUserEventAttendance(string slug) + [ProducesResponseType(StatusCodes.Status200OK)] + public IActionResult ConfirmCurrentUserEventAttendance([FromRoute] string slug) { - throw new NotImplementedException(); + var user = _userService.GetCurrentUser(); + var participant = _participantService.CreateOrUpdateParticipant(new ParticipantDto { EventSlug = slug, Username = user.Username, Status = ParticipantStatus.Participating }); + + return Ok(participant); + } + + [HttpDelete] + [Route("events/{slug}/cancel")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public IActionResult CancelCurrentUserEventAttendance([FromRoute] string slug) + { + var userId = _userService.GetCurrentUser().Id; + _participantService.DeleteParticipant(userId, slug); + + return NoContent(); } [HttpGet] diff --git a/Server/ReasnAPI/ReasnAPI/Controllers/UsersController.cs b/Server/ReasnAPI/ReasnAPI/Controllers/UsersController.cs index cb2849ab..7cda9226 100644 --- a/Server/ReasnAPI/ReasnAPI/Controllers/UsersController.cs +++ b/Server/ReasnAPI/ReasnAPI/Controllers/UsersController.cs @@ -1,47 +1,84 @@ +using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using ReasnAPI.Models.DTOs; +using ReasnAPI.Models.Enums; +using ReasnAPI.Services; namespace ReasnAPI.Controllers; [ApiController] [Route("[controller]")] -public class UsersController : ControllerBase +public class UsersController(UserService userService, InterestService interestService) : ControllerBase { + private readonly UserService _userService = userService; + private readonly InterestService _interestService = interestService; + [HttpGet] [Authorize(Roles = "Admin")] + [ProducesResponseType>(StatusCodes.Status200OK)] public IActionResult GetUsers() { - throw new NotImplementedException(); + var users = _userService.GetUsersByFilter(u => u.IsActive && u.Role != UserRole.Admin); + return Ok(users); } [HttpGet] [Route("{username}")] - public IActionResult GetUserByUsername(string username) + [ProducesResponseType(StatusCodes.Status200OK)] + public IActionResult GetUserByUsername([FromRoute] string username) { - throw new NotImplementedException(); + var user = _userService.GetUserByUsername(username); + + if (user.Role == UserRole.Admin) + { + return Forbid(); + } + + return Ok(user); } [HttpPut] [Authorize] [Route("{username}")] - public IActionResult UpdateUser(string username) + [ProducesResponseType(StatusCodes.Status200OK)] + public IActionResult UpdateUser( + [FromBody] UserDto userDto, + [FromRoute] string username, + [FromServices] IValidator validator) { - throw new NotImplementedException(); - } + validator.ValidateAndThrow(userDto); - [HttpGet] - [Authorize] - [Route("interests")] - public IActionResult GetUsersInterests(string username) - { - throw new NotImplementedException(); - } + var currentUser = _userService.GetCurrentUser(); - [HttpDelete] - [Authorize(Roles = "Admin")] - [Route("interests/{interestId:int}")] - public IActionResult DeleteUserInterest(int interestId) + // Only admins can update other users from this endpoint + if (currentUser.Role != UserRole.Admin) + { + return Forbid(); + } + + var updatedUser = _userService.UpdateUser(username, userDto); + + return Ok(updatedUser); + } + + [HttpGet] + [Authorize] + [Route("interests")] + [ProducesResponseType>(StatusCodes.Status200OK)] + public IActionResult GetUsersInterests() + { + var interests = _interestService.GetAllInterests(); + return Ok(interests); + } + + [HttpDelete] + [Authorize(Roles = "Admin")] + [Route("interests/{interestId:int}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public IActionResult DeleteUserInterest([FromRoute] int interestId) { - throw new NotImplementedException(); + _interestService.DeleteInterest(interestId); + return NoContent(); } } \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/ParticipantMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/ParticipantMapper.cs index 66fee4e6..23e03520 100644 --- a/Server/ReasnAPI/ReasnAPI/Mappers/ParticipantMapper.cs +++ b/Server/ReasnAPI/ReasnAPI/Mappers/ParticipantMapper.cs @@ -8,8 +8,8 @@ public static ParticipantDto ToDto(this Participant participant) { return new ParticipantDto { - EventId = participant.EventId, - UserId = participant.UserId, + EventSlug = participant.Event.Slug, + Username = participant.User.Username, Status = participant.Status }; } @@ -18,14 +18,4 @@ public static List ToDtoList(this IEnumerable parti { return participants.Select(ToDto).ToList(); } - - public static Participant ToEntity(this ParticipantDto participantDto) - { - return new Participant - { - EventId = participantDto.EventId, - UserId = participantDto.UserId, - Status = participantDto.Status - }; - } } \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/UserInterestMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/UserInterestMapper.cs index 19ac06bb..42c9325b 100644 --- a/Server/ReasnAPI/ReasnAPI/Mappers/UserInterestMapper.cs +++ b/Server/ReasnAPI/ReasnAPI/Mappers/UserInterestMapper.cs @@ -19,11 +19,12 @@ public static List ToDtoList(this IEnumerable use return userInterests.Select(ToDto).ToList(); } - public static UserInterest ToEntity(this UserInterestDto userInterestDto) + public static UserInterest ToEntity(this UserInterestDto userInterestDto, int userId, int interestId) { return new UserInterest { - Interest = userInterestDto.Interest.ToEntity(), + UserId = userId, + InterestId = interestId, Level = userInterestDto.Level }; } diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/UserMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/UserMapper.cs index 512756b8..1cd31cbc 100644 --- a/Server/ReasnAPI/ReasnAPI/Mappers/UserMapper.cs +++ b/Server/ReasnAPI/ReasnAPI/Mappers/UserMapper.cs @@ -16,6 +16,7 @@ public static UserDto ToDto(this User user) Phone = user.Phone, Role = user.Role, AddressId = user.AddressId, + Address = user.Address.ToDto(), Interests = user.UserInterests.ToDtoList() }; } diff --git a/Server/ReasnAPI/ReasnAPI/Models/DTOs/ParticipantDto.cs b/Server/ReasnAPI/ReasnAPI/Models/DTOs/ParticipantDto.cs index 55a0b167..910ceab1 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/DTOs/ParticipantDto.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/DTOs/ParticipantDto.cs @@ -4,8 +4,8 @@ namespace ReasnAPI.Models.DTOs { public class ParticipantDto { - public int EventId { get; set; } - public int UserId { get; set; } + public string EventSlug { get; set; } = null!; + public string Username { get; set; } = null!; public ParticipantStatus Status { get; set; } } } \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Models/DTOs/UserDto.cs b/Server/ReasnAPI/ReasnAPI/Models/DTOs/UserDto.cs index 3d16dc47..d3642802 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/DTOs/UserDto.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/DTOs/UserDto.cs @@ -11,6 +11,7 @@ public class UserDto public string? Phone { get; set; } public UserRole Role { get; set; } public int AddressId { get; set; } + public AddressDto Address { get; set; } = null!; public List? Interests { get; set; } } } \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Program.cs b/Server/ReasnAPI/ReasnAPI/Program.cs index f09cfa4a..7599e5d2 100644 --- a/Server/ReasnAPI/ReasnAPI/Program.cs +++ b/Server/ReasnAPI/ReasnAPI/Program.cs @@ -13,7 +13,6 @@ using ReasnAPI.Services; using ReasnAPI.Services.Authentication; using ReasnAPI.Validators; -using ReasnAPI.Services; using Npgsql; using ReasnAPI.Models.Enums; using Microsoft.Extensions.Configuration; @@ -58,6 +57,14 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddValidatorsFromAssemblyContaining(); @@ -72,13 +79,6 @@ options.UseNpgsql(dataSource) .EnableDetailedErrors()); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); - builder.Services.AddControllers(); builder.Services.AddSwaggerGen(options => diff --git a/Server/ReasnAPI/ReasnAPI/Services/EventService.cs b/Server/ReasnAPI/ReasnAPI/Services/EventService.cs index 3e3d3709..0efacdb3 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/EventService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/EventService.cs @@ -21,7 +21,7 @@ public EventDto CreateEvent(EventDto eventDto) newEvent.CreatedAt = createdTime; newEvent.UpdatedAt = createdTime; newEvent.Slug = CreateSlug(eventDto); - + context.Events.Add(newEvent); diff --git a/Server/ReasnAPI/ReasnAPI/Services/ImageService.cs b/Server/ReasnAPI/ReasnAPI/Services/ImageService.cs index 59d688d4..f911fe13 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/ImageService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/ImageService.cs @@ -174,20 +174,16 @@ public ImageDto GetImageById(int id) return imageDto; } - public IEnumerable GetImagesByUserId(int userId) + public ImageDto GetImageByUserId(int userId) { - var images = context.Images - .Where(image => image.ObjectId == userId && image.ObjectType == ObjectType.User) - .ToList(); + var image = context.Images.FirstOrDefault(image => image.ObjectId == userId && image.ObjectType == ObjectType.User); - if (!images.Any()) + if (image is null) { - throw new NotFoundException("Images not found"); + throw new NotFoundException("Image not found"); } - var imageDtos = images.ToDtoList().AsEnumerable(); - - return imageDtos; + return image.ToDto(); } public IEnumerable GetAllImages() diff --git a/Server/ReasnAPI/ReasnAPI/Services/ParameterService.cs b/Server/ReasnAPI/ReasnAPI/Services/ParameterService.cs index f38c8534..9372a992 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/ParameterService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/ParameterService.cs @@ -35,7 +35,7 @@ public ParameterDto UpdateParameter(int parameterId, ParameterDto parameterDto) var parameterCheck = parameters.FirstOrDefault(r => r.Parameters.Any(p => p.Id == parameterId)); - if (parameterCheck is not null) + if (parameterCheck is not null) { throw new BadRequestException("Parameter is associated with an event"); } diff --git a/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs b/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs index 2b568ddb..481bcb5b 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs @@ -1,3 +1,4 @@ +using Microsoft.EntityFrameworkCore; using ReasnAPI.Exceptions; using ReasnAPI.Mappers; using ReasnAPI.Models.Database; @@ -8,84 +9,68 @@ namespace ReasnAPI.Services; public class ParticipantService(ReasnContext context) { - public ParticipantDto CreateParticipant(ParticipantDto participantDto) + public ParticipantDto CreateOrUpdateParticipant(ParticipantDto participantDto) { ArgumentNullException.ThrowIfNull(participantDto); - var participantDb = context.Participants.FirstOrDefault(r => r.Event.Id == participantDto.EventId && r.User.Id == participantDto.UserId); + var eventDb = context.Events.FirstOrDefault(e => e.Slug == participantDto.EventSlug); - if (participantDb is not null) + if (eventDb is null) { - throw new BadRequestException("Participant already exists"); + throw new NotFoundException("Event not found"); } - var userInDb = context.Users.FirstOrDefault(r => r.Id == participantDto.UserId); + var userDb = context.Users.FirstOrDefault(u => u.Username == participantDto.Username); - if (userInDb is null) + if (userDb is null) { throw new NotFoundException("User not found"); } - var eventInDb = context.Events.FirstOrDefault(r => r.Id == participantDto.EventId); + var participant = context.Participants.FirstOrDefault(r => r.Event.Id == eventDb.Id && r.User.Id == userDb.Id); - if (eventInDb is null) + if (participant is null) { - throw new NotFoundException("Event not found"); + context.Participants.Add(new Participant { EventId = eventDb.Id, UserId = userDb.Id, Status = participantDto.Status }); } - context.Participants.Add(participantDto.ToEntity()); - context.SaveChanges(); - - return participantDto; - } - - public ParticipantDto UpdateParticipant(int participantId, ParticipantDto participantDto) - { - ArgumentNullException.ThrowIfNull(participantDto); - - var participant = context.Participants.FirstOrDefault(r => r.Id == participantId); - if (participant is null) + else { - throw new NotFoundException("Participant not found"); + participant.Status = participantDto.Status; + context.Participants.Update(participant); } - participant.Status = participantDto.Status; - - context.Participants.Update(participant); context.SaveChanges(); - return participant.ToDto(); + return participantDto; } - public void DeleteParticipant(int participantId) + public void DeleteParticipant(int userId, string eventSlug) { - var participant = context.Participants.FirstOrDefault(r => r.Id == participantId); + var eventDb = context.Events.FirstOrDefault(e => e.Slug == eventSlug); - if (participant is null) + if (eventDb is null) { - throw new NotFoundException("Participant not found"); + throw new NotFoundException("Event not found"); } - context.Participants.Remove(participant); - context.SaveChanges(); - } - - public ParticipantDto GetParticipantById(int participantId) - { - var participant = context.Participants.Find(participantId); + var participant = context.Participants.FirstOrDefault(r => r.Event.Id == eventDb.Id && r.User.Id == userId); if (participant is null) { throw new NotFoundException("Participant not found"); } - return participant.ToDto(); + context.Participants.Remove(participant); + context.SaveChanges(); } public IEnumerable GetParticipantsByFilter(Expression> filter) { return context.Participants .Where(filter) + .Include(p => p.Event) + .Include(p => p.User) .ToDtoList() .AsEnumerable(); } @@ -93,6 +78,8 @@ public IEnumerable GetParticipantsByFilter(Expression GetAllParticipants() { return context.Participants + .Include(p => p.Event) + .Include(p => p.User) .ToDtoList() .AsEnumerable(); } diff --git a/Server/ReasnAPI/ReasnAPI/Services/UserService.cs b/Server/ReasnAPI/ReasnAPI/Services/UserService.cs index b1b49b88..0981a436 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/UserService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/UserService.cs @@ -3,6 +3,7 @@ using ReasnAPI.Mappers; using ReasnAPI.Models.Database; using ReasnAPI.Models.DTOs; +using Serilog; using System.Linq.Expressions; using System.Security.Claims; using System.Transactions; @@ -50,7 +51,7 @@ public User GetCurrentUser() return user; } - public UserDto UpdateUser(int userId, UserDto userDto) + public UserDto UpdateUser(string username, UserDto userDto) { using (var scope = new TransactionScope()) { @@ -59,46 +60,50 @@ public UserDto UpdateUser(int userId, UserDto userDto) var user = _context.Users .Include(u => u.UserInterests) .ThenInclude(ui => ui.Interest) - .FirstOrDefault(r => r.Id == userId); + .FirstOrDefault(r => r.Username == username); if (user is null) { throw new NotFoundException("User not found"); } - var usernameExists = _context.Users.Any(r => r.Username == userDto.Username && r.Id != userId); + var usernameExists = _context.Users.Any(r => r.Username == userDto.Username && r.Id != user.Id); if (usernameExists) { throw new BadRequestException("User with given username already exists"); } - var emailExists = _context.Users.Any(r => r.Email == userDto.Email && r.Id != userId); + var emailExists = _context.Users.Any(r => r.Email == userDto.Email && r.Id != user.Id); if (emailExists) { throw new BadRequestException("User with given email already exists"); } - var phoneExists = _context.Users.Any(r => r.Phone == userDto.Phone && r.Id != userId); + var phoneExists = _context.Users.Any(r => r.Phone == userDto.Phone && r.Id != user.Id); if (phoneExists) { throw new BadRequestException("User with given phone number already exists"); } - user.Username = userDto.Username; user.Name = userDto.Name; user.Surname = userDto.Surname; user.Username = userDto.Username; user.Email = userDto.Email; user.Phone = userDto.Phone; user.Role = userDto.Role; - user.AddressId = userDto.AddressId; user.UpdatedAt = DateTime.UtcNow; _context.Users.Update(user); + // Get list of interests to remove + var interestsToRemove = user.UserInterests + .Where(ui => !userDto.Interests!.Exists(uid => uid.Interest.Name == ui.Interest.Name)); + + _context.UserInterests.RemoveRange(interestsToRemove); + if (userDto.Interests is null || userDto.Interests.Count == 0) { _context.SaveChanges(); @@ -106,18 +111,7 @@ public UserDto UpdateUser(int userId, UserDto userDto) return userDto; } - var interestsToRemove = user.UserInterests - .Where(ui => !userDto.Interests.Exists(uid => uid.Interest.Name == ui.Interest.Name)); - - _context.UserInterests.RemoveRange(interestsToRemove); - - var interestsToAdd = userDto.Interests - .Where(uid => !user.UserInterests.Any(ui => ui.Interest.Name == uid.Interest.Name)) - .Select(uid => uid.ToEntity()) - .ToList(); - - _context.UserInterests.AddRange(interestsToAdd); - + // Get list of interests to update var interestsToUpdate = user.UserInterests .Where(ui => userDto.Interests.Exists(uid => uid.Interest.Name == ui.Interest.Name)) .ToList(); @@ -135,6 +129,20 @@ public UserDto UpdateUser(int userId, UserDto userDto) _context.UserInterests.Update(interest); } + // Get list of existing interests in the database + var existingInterests = _context.Interests.ToList(); + + // Get list of interests to add + // Look for interests that are not already in the user's interests + var interestsToAdd = userDto.Interests + .Where(uid => !user.UserInterests.Any(ui => ui.Interest.Name == uid.Interest.Name)) + .Select(uid => uid.ToEntity(user.Id, existingInterests.Find(ei => ei.Name == uid.Interest.Name)!.Id)) + .ToList(); + + // Update interests for + interestsToAdd.ForEach(user.UserInterests.Add); + _context.Users.Update(user); + _context.SaveChanges(); scope.Complete(); } @@ -145,7 +153,9 @@ public UserDto UpdateUser(int userId, UserDto userDto) public UserDto GetUserById(int userId) { var user = _context.Users + .Include(a => a.Address) .Include(u => u.UserInterests) + .ThenInclude(ui => ui.Interest) .FirstOrDefault(u => u.Id == userId); if (user is null) @@ -159,7 +169,9 @@ public UserDto GetUserById(int userId) public UserDto GetUserByUsername(string username) { var user = _context.Users + .Include(a => a.Address) .Include(u => u.UserInterests) + .ThenInclude(ui => ui.Interest) .FirstOrDefault(u => u.Username == username); if (user is null) @@ -173,6 +185,7 @@ public UserDto GetUserByUsername(string username) public IEnumerable GetUsersByFilter(Expression> filter) { return _context.Users + .Include(a => a.Address) .Include(u => u.UserInterests) .ThenInclude(ui => ui.Interest) .Where(filter) @@ -183,6 +196,7 @@ public IEnumerable GetUsersByFilter(Expression> filter public IEnumerable GetAllUsers() { var users = _context.Users + .Include(a => a.Address) .Include(u => u.UserInterests) .ThenInclude(ui => ui.Interest) .ToList();