From 3599d8750c5d1d433f467f542d36f17167696dba Mon Sep 17 00:00:00 2001 From: Owczarek Kamil <126111610+bilimig@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:03:17 +0200 Subject: [PATCH] [RSN-37] - Created services for Event Tag Parameter Interest Image (#42) * Created CRUD services for Parameter, Status, Tag and C&U for Event * created CRUD Image, Interest, Event and Parameter Services Also added geting all elements and by filter(not done properly yet) * changed delete services to void isnted of bool * Fixed GetByFilter service in Event, Image, Intrest, Parameter, Status and Tag Services * little changes * fixing build issue and changing list to ienumerable * Fixing EventServices and standardizing syntax issues * created test for interest status parameter and tag * Created ImageServicesTests * Updated Parameter, Tag, Status services Checked if any of Parameters, Tags or Statuses are used before delating/updating. * fixed bug in imageservicetest * one more fix * Updated services and tests Changed delete methods to bool, updated mocks on tests. * Updated services * Updates after scafoled nr 1 * Updated services Changed using bridge tables to using colections in EF. * Updated EventService added a logic for parameters in event * working event services * created mappers and small fixes * finished mappers * Added exceptions in services * fixed mappers according to our convension * Changed exceptions directory location * fixed build error * Exceptions changes * Unit tests fix * updated services * Slug creating method updated * Update ImageService.cs * Update ImageService.cs * updated image services and tests also updated slug method for event services * event service delete method updated * refactor and fixes * refactor * Update EventService.cs * last refactor * removing spaces * added geteventbyslug method to eventservice * Update ImageService.cs * refactor * changed list to Enumerable * changed list to enumerable * folder refactor * updated services * Update EventService.cs * changes * Update EventServicesTest.cs * updates * Update EventService.cs * Update EventService.cs * Update ImageService.cs * Updates * Update EventMapper.cs * changes * Update ImageServiceTests.cs * changes * Formating * Update EventService.cs * Update EventService.cs * changes * Created AddEventComment method * Update EventService.cs * fixed updating and creating event * Update EventService.cs * changed create slug and text for event --------- Co-authored-by: Maciej Koperdowski --- .../Services/EventServicesTest.cs | 331 ++++++++++++++++++ .../ReasnAPI.Tests/Services/FakeDbSet.cs | 17 +- .../Services/ImageServiceTests.cs | 254 ++++++++++++++ .../Services/InterestServiceTests.cs | 195 +++++++++++ .../Services/ParameterServiceTests.cs | 218 ++++++++++++ .../Services/TagServiceTests.cs | 161 +++++++++ .../ReasnAPI/ReasnAPI/Mappers/EventMapper.cs | 50 +++ .../ReasnAPI/ReasnAPI/Mappers/ImageMapper.cs | 34 ++ .../ReasnAPI/Mappers/InterestMapper.cs | 39 ++- .../ReasnAPI/Mappers/ParameterMapper.cs | 36 ++ Server/ReasnAPI/ReasnAPI/Mappers/TagMapper.cs | 34 ++ .../ReasnAPI/Models/DTOs/InterestDto.cs | 6 +- .../ReasnAPI/Models/Database/Event.cs | 1 + .../Models/Database/EventParameter.cs | 15 - .../ReasnAPI/Models/Database/EventTag.cs | 15 - .../ReasnAPI/Models/Database/ReasnContext.cs | 3 +- Server/ReasnAPI/ReasnAPI/Program.cs | 37 +- .../ReasnAPI/Services/EventService.cs | 311 +++++++++++++++- .../ReasnAPI/Services/ImageService.cs | 232 +++++++++++- .../ReasnAPI/Services/InterestService.cs | 100 ++++++ .../ReasnAPI/Services/IntrestService.cs | 16 - .../ReasnAPI/Services/ParameterService.cs | 173 ++++++++- .../ReasnAPI/ReasnAPI/Services/TagService.cs | 149 +++++++- 23 files changed, 2283 insertions(+), 144 deletions(-) create mode 100644 Server/ReasnAPI/ReasnAPI.Tests/Services/EventServicesTest.cs create mode 100644 Server/ReasnAPI/ReasnAPI.Tests/Services/ImageServiceTests.cs create mode 100644 Server/ReasnAPI/ReasnAPI.Tests/Services/InterestServiceTests.cs create mode 100644 Server/ReasnAPI/ReasnAPI.Tests/Services/ParameterServiceTests.cs create mode 100644 Server/ReasnAPI/ReasnAPI.Tests/Services/TagServiceTests.cs create mode 100644 Server/ReasnAPI/ReasnAPI/Mappers/EventMapper.cs create mode 100644 Server/ReasnAPI/ReasnAPI/Mappers/ImageMapper.cs create mode 100644 Server/ReasnAPI/ReasnAPI/Mappers/ParameterMapper.cs create mode 100644 Server/ReasnAPI/ReasnAPI/Mappers/TagMapper.cs delete mode 100644 Server/ReasnAPI/ReasnAPI/Models/Database/EventParameter.cs delete mode 100644 Server/ReasnAPI/ReasnAPI/Models/Database/EventTag.cs create mode 100644 Server/ReasnAPI/ReasnAPI/Services/InterestService.cs delete mode 100644 Server/ReasnAPI/ReasnAPI/Services/IntrestService.cs diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/EventServicesTest.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/EventServicesTest.cs new file mode 100644 index 00000000..fc504b17 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/EventServicesTest.cs @@ -0,0 +1,331 @@ +using ReasnAPI.Models.DTOs; +using ReasnAPI.Services; +using ReasnAPI.Models.Database; +using Moq; +using Moq.EntityFrameworkCore; +using ReasnAPI.Models.Enums; +using ReasnAPI.Exceptions; + + +namespace ReasnAPI.Tests.Services +{ + [TestClass] + public class EventServicesTest + { + + [TestMethod] + public void UpdateEvent_EventExists_EventUpdated() + { + var tagDto = new TagDto() { Name = "tesTag" }; + var tagList = new List + { + tagDto + }; + var eventDto = new EventDto() + { + Name = "name1", + AddressId = 1, + Description = "description2", + OrganizerId = 1, + StartAt = DateTime.Now, + EndAt = DateTime.Now, + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now, + Slug = "name1", + Status = EventStatus.Completed, + Tags = tagList, + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Events).ReturnsDbSet(new List{ + new Event() + { + Id = 1, + Name = "name", + AddressId = 1, + Description = "description", + OrganizerId = 1, + StartAt = DateTime.Now, + EndAt = DateTime.Now, + CreatedAt = DateTime.Now, + Slug = "name1", + UpdatedAt = DateTime.Now, + Status = EventStatus.Completed, + }}); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List()); + + mockContext.Setup(c => c.Addresses).ReturnsDbSet(new List
{ + new Address() + { + Id = 1, + City = "city", + Country = "country", + State = "state", + Street = "street", + ZipCode = "test123" + }}); + mockContext.Setup(c => c.Users).ReturnsDbSet(new List{ + new User() + { + Id = 1, + Name = "test", + Email = "test@wp.pl", + AddressId = 1, + CreatedAt = DateTime.Now, + IsActive = true, Role = UserRole.Admin, + Password ="test123", + Phone = "123123123", + Surname ="test", + Username ="test", + UpdatedAt =DateTime.Now }}); + + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List()); + mockContext.Setup(c => c.Comments).ReturnsDbSet(new List()); + mockContext.Setup(c => c.Participants).ReturnsDbSet(new List()); + var eventService = new EventService(mockContext.Object, new ParameterService(mockContext.Object), new TagService(mockContext.Object), new CommentService(mockContext.Object)); + + var result = eventService.UpdateEvent(1, eventDto); + Assert.AreEqual("name1", result.Name); + Assert.AreEqual("description2", result.Description); + Assert.AreEqual(1, result.Tags.Count); + + } + + [TestMethod] + public void UpdateEvent_EventDoesNotExist_NullReturned() + { + var tagDto = new TagDto() { Name = "tesTag" }; + var tagList = new List + { + tagDto + }; + var eventDto = new EventDto() + { + Name = "name1", + AddressId = 1, + Description = "description2", + OrganizerId = 1, + StartAt = DateTime.Now, + EndAt = DateTime.Now, + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now, + Status = EventStatus.Completed, + Tags = tagList, + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Events).ReturnsDbSet(new List()); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List()); + + mockContext.Setup(c => c.Addresses).ReturnsDbSet(new List
{ + new Address() + { + Id = 1, + City = "city", + Country = "country", + State = "state", + Street = "street", + ZipCode = "test123" + }}); + mockContext.Setup(c => c.Users).ReturnsDbSet(new List{ + new User() + { + Id = 1, + Name = "test", + Email = "test@wp.pl", + AddressId = 1, + CreatedAt = DateTime.Now, + IsActive = true, + Role = UserRole.Admin, + Password ="test123", + Phone = "123123123", + Surname ="test", + Username ="test", + UpdatedAt =DateTime.Now + }}); + + var eventService = new EventService(mockContext.Object, new ParameterService(mockContext.Object), new TagService(mockContext.Object), new CommentService(mockContext.Object)); + + Assert.ThrowsException(() => eventService.UpdateEvent(1, eventDto)); + } + + [TestMethod] + public void GetEventById_EventExists_EventReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Events).ReturnsDbSet(new List + { + new Event() + { + Id = 1, + Name = "name", + Slug = "name", + AddressId = 1, + Description = "description", + OrganizerId = 1, + StartAt = DateTime.Now, + EndAt = DateTime.Now, + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now, + Status = EventStatus.Completed, + }}); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List + { + new Tag() + { + Id = 1, + Name = "name" + } + }); + + mockContext.Setup(c => c.Addresses).ReturnsDbSet(new List
{ + new Address() + { + Id = 1, + City = "city", + Country = "country", + State = "state", + Street = "street", + ZipCode = "test123" + }}); + mockContext.Setup(c => c.Users).ReturnsDbSet(new List{ + new User() + { + Id = 1, + Name = "test", + Email = "test@wp.pl", + AddressId = 1, + CreatedAt = DateTime.Now, + IsActive = true, + Role = UserRole.User, + Password ="test123", + Phone = "123123123", + Surname ="test", + Username ="test", + UpdatedAt =DateTime.Now }}); + + + var eventService = new EventService(mockContext.Object, new ParameterService(mockContext.Object), new TagService(mockContext.Object), new CommentService(mockContext.Object)); + + var result = eventService.GetEventById(1); + Assert.IsNotNull(result); + + } + + [TestMethod] + + public void GetEventById_EventDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Events).ReturnsDbSet(new List()); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List()); + + mockContext.Setup(c => c.Addresses).ReturnsDbSet(new List
+ { + new Address() + { + Id = 1, + City = "city", + Country = "country", + State = "state", + Street = "street", + ZipCode = "test123" + }}); + mockContext.Setup(c => c.Users).ReturnsDbSet(new List + { + new User() + { + Id = 1, + Name = "test", + Email = "test@wp.pl", + AddressId = 1, + CreatedAt = DateTime.Now, + IsActive = true, + Role = UserRole.Admin, + Password ="test123", + Phone = "123123123", + Surname ="test", + Username ="test", + UpdatedAt =DateTime.Now + }}); + + + var eventService = new EventService(mockContext.Object, new ParameterService(mockContext.Object), new TagService(mockContext.Object), new CommentService(mockContext.Object)); + + Assert.ThrowsException(() => eventService.GetEventById(1)); + } + + [TestMethod] + + public void DeleteEvent_EventExists_EventDeleted() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Events).ReturnsDbSet(new List + { + new Event() + { + Id = 1, + Name = "name", + Slug = "name", + AddressId = 1, + Description = "description", + OrganizerId = 1, + StartAt = DateTime.Now, + EndAt = DateTime.Now, + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now, + Status = EventStatus.Completed, + Tags = new List { new Tag { Id = 1, Name = "name" } }, + Parameters = new List { new Parameter { Key = "key", Value = "value" } }, + Comments = new List { new Comment { Id = 1, Content = "content" } }, + Participants = new List { new Participant { Id = 1, UserId = 1 } } + } + }); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List + { + new Tag() + { + Id = 1, + Name = "name" + } + }); + mockContext.Setup(c => c.Addresses).ReturnsDbSet(new List
+ { + new Address() + { + Id = 1, + City = "city", + Country = "country", + State = "state", + Street = "street", + ZipCode = "test123" + } + }); + mockContext.Setup(c => c.Users).ReturnsDbSet(new List{ + new User() + { + Id = 1, + Name = "test", + Email = "test@wp.pl", + AddressId = 1, + CreatedAt = DateTime.Now, + IsActive = true, + Role = UserRole.User, + Password ="test123", + Phone = "123123123", + Surname ="test", + Username ="test", + UpdatedAt =DateTime.Now }}); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List()); + mockContext.Setup(c => c.Comments).ReturnsDbSet(new List()); + mockContext.Setup(c => c.Participants).ReturnsDbSet(new List()); + + var eventService = new EventService(mockContext.Object, new ParameterService(mockContext.Object), new TagService(mockContext.Object), new CommentService(mockContext.Object)); + + eventService.DeleteEvent(1); + + mockContext.Verify(c => c.SaveChanges(), Times.AtLeastOnce); + } + + } +} diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/FakeDbSet.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/FakeDbSet.cs index d8c253a3..49312afb 100644 --- a/Server/ReasnAPI/ReasnAPI.Tests/Services/FakeDbSet.cs +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/FakeDbSet.cs @@ -10,10 +10,10 @@ public class FakeDbSet : DbSet, IAsyncEnumerable public FakeDbSet() { - _data = []; + _data = new List(); } - public override T? Find(params object[] keyValues) + public override T Find(params object[] keyValues) { return _data.FirstOrDefault(); } @@ -24,18 +24,21 @@ public override EntityEntry Add(T entity) return null; // Return null for simplicity, adjust as needed } - public override IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { return new AsyncEnumerator(_data.GetEnumerator()); } public override Microsoft.EntityFrameworkCore.Metadata.IEntityType EntityType => throw new NotImplementedException(); - // Implement other methods... - - private class AsyncEnumerator(IEnumerator enumerator) : IAsyncEnumerator + private class AsyncEnumerator : IAsyncEnumerator { - private readonly IEnumerator _enumerator = enumerator; + private readonly IEnumerator _enumerator; + + public AsyncEnumerator(IEnumerator enumerator) + { + _enumerator = enumerator; + } public ValueTask DisposeAsync() { diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/ImageServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/ImageServiceTests.cs new file mode 100644 index 00000000..746f9b9f --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/ImageServiceTests.cs @@ -0,0 +1,254 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using ReasnAPI.Services; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; +using System.Linq.Expressions; +using Moq; +using Moq.EntityFrameworkCore; +using ReasnAPI.Exceptions; +using ReasnAPI.Models.Enums; + + +namespace ReasnAPI.Tests.Services +{ + [TestClass] + public class ImageServiceTests + { + + [TestMethod] + public void CreateImage_ImageDoesNotExist_ImageCreated() + { + var imageDto = new ImageDto + { + ImageData = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Images).ReturnsDbSet(new List()); + + var imageService = new ImageService(mockContext.Object); + List imagedtoslist = new List(); + imagedtoslist.Add(imageDto); // Use Add instead of Append + + var result = imageService.CreateImages(imagedtoslist); + Assert.IsNotNull(result); + } + + [TestMethod] + public void CreateImage_ImageExists_ImageNotCreated() + { + var imageDto = new ImageDto + { + ImageData = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Images).ReturnsDbSet(new List + { new Image { ImageData = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 } } }); + + var imageService = new ImageService(mockContext.Object); + List imagedtoslist = new List(); + imagedtoslist.Add(imageDto); // Use Add instead of Append + + var result = imageService.CreateImages(imagedtoslist); + + mockContext.Verify(m => m.Add(It.IsAny()), Times.Never()); + mockContext.Verify(m => m.AddRange(It.IsAny>()), Times.Never()); + } + + [TestMethod] + public void GetAllImages_ImageExists_ImagesReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Images).ReturnsDbSet(new List + { new Image { ImageData = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 } } }); + + var imageService = new ImageService(mockContext.Object); + + var result = imageService.GetAllImages(); + + Assert.IsNotNull(result); + } + + [TestMethod] + public void GetAllImages_ImageNotExists_EmptyListReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Images).ReturnsDbSet(new List()); + + var imageService = new ImageService(mockContext.Object); + + var result = imageService.GetAllImages().ToList(); + + Assert.AreEqual(0, result.Count); + } + + [TestMethod] + public void GetImageById_ImageExists_ImageReturned() + { + + var imaDto = new ImageDto() + { + ImageData = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 } + }; + + var fakeStatuses = new FakeDbSet(); + fakeStatuses.Add(new Image() { Id = 1, ImageData = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 } }); + + var mockContext = new Mock(); + mockContext.Setup(c => c.Images).Returns(fakeStatuses); + + var imageService = new ImageService(mockContext.Object); + + var result = imageService.GetImageById(1); + Assert.IsNotNull(result); + } + + [TestMethod] + public void GetImageById_ImageDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Images).ReturnsDbSet(new List()); + + var imageService = new ImageService(mockContext.Object); + + Assert.ThrowsException(() => imageService.GetImageById(1)); + } + + [TestMethod] + public void UpdateImages_UserType_ImageUpdated() + { + var imageDto = new ImageDto + { + ObjectId = 1, + ObjectType = ObjectType.User, + ImageData = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 } + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Images).ReturnsDbSet(new List + { new Image { ObjectId = 1, ObjectType = ObjectType.User, ImageData = new byte[] { } } }); + + var imageService = new ImageService(mockContext.Object); + + imageService.UpdateImageForUser(1, imageDto); + + mockContext.Verify(c => c.SaveChanges(), Times.Once); + } + + [TestMethod] + public void UpdateImages_EventType_ImagesUpdated() + { + var imageDtos = new List + { + new ImageDto + { + ObjectId = 1, + ObjectType = ObjectType.Event, + ImageData = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 } + }, + new ImageDto + { + ObjectId = 1, + ObjectType = ObjectType.Event, + ImageData = new byte[] { 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 } + } + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Images).ReturnsDbSet(new List + { new Image { ObjectId = 1, ObjectType = ObjectType.Event, ImageData = new byte[] { } } }); + + var imageService = new ImageService(mockContext.Object); + + imageService.UpdateImagesForEvent(1, imageDtos); + + mockContext.Verify(c => c.SaveChanges(), Times.Once); + } + + [TestMethod] + public void UpdateImages_ImageDoesNotExist_ExceptionThrown() + { + var imageDto = new ImageDto + { + ObjectId = 1, + ObjectType = ObjectType.User, + ImageData = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 } + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Images).ReturnsDbSet(new List()); + + var imageService = new ImageService(mockContext.Object); + + Assert.ThrowsException(() => imageService.UpdateImagesForEvent(1, new List { imageDto })); + } + + [TestMethod] + + public void DeleteImage_ImageExists_ImageDeleted() + { + var mockContext = new Mock(); + + mockContext.Setup(c => c.Images).ReturnsDbSet(new List + { new Image { Id = 1, ImageData = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 } } }); + + var imageService = new ImageService(mockContext.Object); + + imageService.DeleteImageById(1); + + mockContext.Verify(c => c.SaveChanges(), Times.Once); + } + + [TestMethod] + + public void DeleteImage_ImageDoesNotExist_NothingDeleted() + { + var mockContext = new Mock(); + + mockContext.Setup(c => c.Images).ReturnsDbSet(new List()); + + var imageService = new ImageService(mockContext.Object); + + Assert.ThrowsException(() => imageService.DeleteImageById(1)); + } + + [TestMethod] + + public void GetImageByFilter_ImageExists_ImageReturned() + { + var mockContext = new Mock(); + + mockContext.Setup(c => c.Images).ReturnsDbSet(new List + { new Image { Id = 1, ImageData = new byte[] { } } }); + + var imageService = new ImageService(mockContext.Object); + + var result = imageService.GetImagesByFilter(i => i.Id == 1).ToList(); + + Assert.IsNotNull(result); + } + + [TestMethod] + + public void GetImageByFilter_ImageDoesNotExist_NothingReturned() + { + var mockContext = new Mock(); + + mockContext.Setup(c => c.Images).ReturnsDbSet(new List()); + + var imageService = new ImageService(mockContext.Object); + + var result = imageService.GetImagesByFilter(i => i.Id == 1).ToList(); + + Assert.AreEqual(0, result.Count()); + } + + + } +} diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/InterestServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/InterestServiceTests.cs new file mode 100644 index 00000000..ba92cb9d --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/InterestServiceTests.cs @@ -0,0 +1,195 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using ReasnAPI.Services; +using Moq; +using Moq.EntityFrameworkCore; +using ReasnAPI.Exceptions; + +namespace ReasnAPI.Tests.Services +{ + [TestClass] + public class InterestServiceTests + { + [TestMethod] + public void CreateInterest_InterestDoesNotExist_InterestCreated() + { + var interestDto = new InterestDto + { + Name = "TestInterest" + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List()); + + var interestService = new InterestService(mockContext.Object); + + var result = interestService.CreateInterest(interestDto); + + Assert.AreEqual(interestDto.Name, result.Name); + } + + [TestMethod] + public void CreateInterest_InterestExists_InterestNotCreated() + { + var interestDto = new InterestDto + { + Name = "TestInterest" + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List { new Interest { Name = "TestInterest" } }); + + var interestService = new InterestService(mockContext.Object); + + Assert.ThrowsException(() => interestService.CreateInterest(interestDto)); + } + + [TestMethod] + public void GetAllInterests_InterestExists_InterestsReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List { new Interest { Name = "TestInterest" } }); + + var interestService = new InterestService(mockContext.Object); + + var result = interestService.GetAllInterests(); + + Assert.AreEqual(1, result.Count()); + } + + [TestMethod] + public void GetAllInterests_InterestDoesNotExists_NothingReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List()); + + var interestService = new InterestService(mockContext.Object); + + var result = interestService.GetAllInterests(); + + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void GetInterestById_InterestDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List()); + + var interestService = new InterestService(mockContext.Object); + + Assert.ThrowsException(() => interestService.GetInterestById(1)); + } + + [TestMethod] + public void UpdateInterest_InterestExists_InterestUpdated() + { + var interestDto = new InterestDto + { + Name = "TestInterest1" + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List { new Interest { Id = 1, Name = "TestInterest" } }); + + var interestService = new InterestService(mockContext.Object); + + var result = interestService.UpdateInterest(1, interestDto); + + Assert.AreEqual("TestInterest1", result.Name); + } + + [TestMethod] + public void UpdateInterest_InterestDoesNotExist_NullReturned() + { + var interestDto = new InterestDto + { + Name = "TestInterest" + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List()); + + var interestService = new InterestService(mockContext.Object); + + Assert.ThrowsException(() => interestService.UpdateInterest(1, interestDto)); + } + + [TestMethod] + public void DeleteInterest_InterestExists_InterestDeleted() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List + { + new Interest { Id = 1, Name = "TestInterest" } + }); + mockContext.Setup(c => c.UserInterests).ReturnsDbSet(new List()); + + var interestService = new InterestService(mockContext.Object); + + interestService.DeleteInterest(1); + + mockContext.Verify(c => c.Interests.Remove(It.Is(i => i.Id == 1)), Times.Once); + mockContext.Verify(c => c.SaveChanges(), Times.Once); + } + + [TestMethod] + public void DeleteInterest_InterestDoesNotExist_NothingDeleted() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List()); + mockContext.Setup(c => c.UserInterests).ReturnsDbSet(new List()); + + var interestService = new InterestService(mockContext.Object); + + Assert.ThrowsException(() => interestService.DeleteInterest(1)); + } + + [TestMethod] + public void DeleteInterest_InterestHasUserInterests_NothingDeleted() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List + { + new Interest { Id = 1, Name = "TestInterest" } + }); + mockContext.Setup(c => c.UserInterests).ReturnsDbSet(new List + { + new UserInterest { InterestId = 1, UserId = 1 } + }); + + Assert.ThrowsException(() => new InterestService(mockContext.Object).DeleteInterest(1)); + + mockContext.Verify(c => c.Interests.Remove(It.IsAny()), Times.Never); + mockContext.Verify(c => c.SaveChanges(), Times.Never); + } + + [TestMethod] + public void GetInterestsByFilter_InterestExists_InterestReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List { new Interest { Name = "TestInterest" } }); + + var interestService = new InterestService(mockContext.Object); + + var result = interestService.GetInterestsByFilter(i => i.Name == "TestInterest").ToList(); + + Assert.AreEqual(1, result.Count()); + + } + + [TestMethod] + public void GetInterestsByFilter_InterestDoesNotExist_NothingReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Interests).ReturnsDbSet(new List()); + + var interestService = new InterestService(mockContext.Object); + + var result = interestService.GetInterestsByFilter(i => i.Name == "TestInterest").ToList(); + + Assert.AreEqual(0, result.Count()); + } + + } +} diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/ParameterServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/ParameterServiceTests.cs new file mode 100644 index 00000000..a7ce743f --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/ParameterServiceTests.cs @@ -0,0 +1,218 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using ReasnAPI.Services; +using Moq; +using Moq.EntityFrameworkCore; +using ReasnAPI.Exceptions; + +namespace ReasnAPI.Tests.Services +{ + [TestClass] + public class ParameterServiceTests + { + [TestMethod] + public void CreateParameter_ParameterDoesNotExist_ParameterCreated() + { + var parameterDto = new ParameterDto + { + Key = "TestKey", + Value = "TestValue" + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List()); + + var parameterService = new ParameterService(mockContext.Object); + + var result = parameterService.CreateParameter(parameterDto); + + Assert.AreEqual(parameterDto.Value, result.Value); + } + + [TestMethod] + public void CreateParameter_ParameterExists_ParameterNotCreated() + { + var parameterDto = new ParameterDto + { + Key = "TestKey", + Value = "TestValue" + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List { new Parameter { Key = "TestKey", Value = "TestValue" } }); + + var parameterService = new ParameterService(mockContext.Object); + + Assert.ThrowsException(() => parameterService.CreateParameter(parameterDto)); + } + + [TestMethod] + public void UpdateParameter_ParameterExists_ParameterUpdated() + { + // Arrange + var parameterDto = new ParameterDto + { + Key = "UpdatedKey", + Value = "UpdatedValue" + }; + + var existingParameter = new Parameter { Id = 1, Key = "TestKey", Value = "TestValue" }; + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List { existingParameter }); + mockContext.Setup(c => c.Events).ReturnsDbSet(new List()); // No events associated + + var parameterService = new ParameterService(mockContext.Object); + + // Act + var result = parameterService.UpdateParameter(1, parameterDto); + + // Assert + Assert.IsNotNull(result); + Assert.AreEqual(parameterDto.Value, result.Value); + Assert.AreEqual(parameterDto.Key, result.Key); + Assert.AreEqual(parameterDto.Value, existingParameter.Value); // Verify the parameter was updated + mockContext.Verify(c => c.SaveChanges(), Times.Once); // Ensure SaveChanges was called + } + + [TestMethod] + public void UpdateParameter_ParameterDoesNotExist_NullReturned() + { + var parameterDto = new ParameterDto + { + Key = "TestKey", + Value = "TestValue" + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List()); // No parameters in context + mockContext.Setup(c => c.Events).ReturnsDbSet(new List()); // No events associated + + var parameterService = new ParameterService(mockContext.Object); + + Assert.ThrowsException(() => parameterService.UpdateParameter(1, parameterDto)); + mockContext.Verify(c => c.SaveChanges(), Times.Never); // Ensure SaveChanges was never called + } + + [TestMethod] + public void UpdateParameter_ParameterInUse_NullReturned() + { + var parameterDto = new ParameterDto + { + Key = "UpdatedKey", + Value = "UpdatedValue" + }; + + var existingParameter = new Parameter { Id = 1, Key = "TestKey", Value = "TestValue" }; + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List { existingParameter }); + mockContext.Setup(c => c.Events).ReturnsDbSet(new List + { + new Event { Parameters = new List { existingParameter } } + }); // Parameter is associated with an event + + var parameterService = new ParameterService(mockContext.Object); + + Assert.ThrowsException(() => parameterService.UpdateParameter(1, parameterDto)); + mockContext.Verify(c => c.SaveChanges(), Times.Never); // Ensure SaveChanges was never called + } + + [TestMethod] + public void GetParameterById_ParameterDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List()); + + var parameterService = new ParameterService(mockContext.Object); + + Assert.ThrowsException(() => parameterService.GetParameterById(1)); + } + + [TestMethod] + public void DeleteParameter_ParameterExists_ParameterDeleted() + { + // Arrange + var parameters = new List + { + new Parameter { Id = 1, Key = "TestKey", Value = "TestValue" } + }; + var events = new List(); + + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(parameters); + mockContext.Setup(c => c.Events).ReturnsDbSet(events); + + var parameterService = new ParameterService(mockContext.Object); + + // Act + parameterService.DeleteParameter(1); + + // Assert + mockContext.Verify(c => c.SaveChanges(), Times.Once); + } + + [TestMethod] + public void DeleteParameter_ParameterDoesNotExist_NothingDeleted() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List()); + var parameterService = new ParameterService(mockContext.Object); + + Assert.ThrowsException(() => parameterService.DeleteParameter(1)); + + mockContext.Verify(c => c.SaveChanges(), Times.Never); + } + + [TestMethod] + public void GetParameterByFilter_ParameterExists_ParameterReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List { new Parameter { Id = 1, Key = "TestKey", Value = "TestValue" } }); + + var parameterService = new ParameterService(mockContext.Object); + + var result = parameterService.GetParametersByFilter(p => p.Key == "TestKey").ToList(); + + Assert.AreEqual(1, result.Count()); + } + + [TestMethod] + public void GetParameterByFilter_ParameterDoesNotExist_NothingReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List()); + + var parameterService = new ParameterService(mockContext.Object); + + var result = parameterService.GetParametersByFilter(p => p.Key == "TestKey").ToList(); + + Assert.AreEqual(0, result.Count()); + } + + [TestMethod] + public void GetAllParameters_ParameterExists_ParameterReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List { new Parameter { Id = 1, Key = "TestKey", Value = "TestValue" } }); + + var parameterService = new ParameterService(mockContext.Object); + + var result = parameterService.GetAllParameters().ToList(); + + Assert.AreEqual(1, result.Count()); + } + + [TestMethod] + public void GetAllParameters_ParameterDoesNotExist_NothingReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Parameters).ReturnsDbSet(new List()); + + var parameterService = new ParameterService(mockContext.Object); + + var result = parameterService.GetAllParameters().ToList(); + + Assert.AreEqual(0, result.Count()); + } + + } +} diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/TagServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/TagServiceTests.cs new file mode 100644 index 00000000..f5d09b50 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/TagServiceTests.cs @@ -0,0 +1,161 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using ReasnAPI.Services; +using Moq; +using Moq.EntityFrameworkCore; +using ReasnAPI.Exceptions; + +namespace ReasnAPI.Tests.Services +{ + [TestClass] + public class TagServiceTests + { + [TestMethod] + public void CreateTag_TagDoesNotExist_TagCreated() + { + var tagDto = new TagDto + { + Name = "TestTag" + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List()); + + var tagService = new TagService(mockContext.Object); + + var result = tagService.CreateTag(tagDto); + + Assert.AreEqual(tagDto.Name, result.Name); + } + + [TestMethod] + public void CreateTag_TagExists_TagNotCreated() + { + var tagDto = new TagDto + { + Name = "TestTag" + }; + + var mockContext = new Mock(); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List { new Tag { Name = "TestTag" } }); + + var tagService = new TagService(mockContext.Object); + + Assert.ThrowsException(() => tagService.CreateTag(tagDto)); + } + + [TestMethod] + public void GetAllTags_TagExists_TagsReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List { new Tag { Name = "TestTag" } }); + + var tagService = new TagService(mockContext.Object); + + var result = tagService.GetAllTags().ToList(); + + Assert.AreEqual(1, result.Count); + } + + [TestMethod] + public void GetAllTags_TagNotExists_EmptyListReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List()); + + var tagService = new TagService(mockContext.Object); + + var result = tagService.GetAllTags().ToList(); + + Assert.AreEqual(0, result.Count); + } + + [TestMethod] + public void GetTagById_TagDoesNotExist_NullReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List()); + + var tagService = new TagService(mockContext.Object); + + Assert.ThrowsException(() => tagService.GetTagById(1)); + } + + [TestMethod] + public void DeleteTag_TagExists_TagDeleted() + { + // Arrange + var tags = new List { new Tag { Id = 1, Name = "TestTag" } }; + var mockContext = new Mock(); + mockContext.Setup(c => c.Tags).ReturnsDbSet(tags); + mockContext.Setup(c => c.Events).ReturnsDbSet(new List()); // No events associated with tags + + var tagService = new TagService(mockContext.Object); + + // Act + tagService.DeleteTag(1); + + // Assert + mockContext.Verify(c => c.SaveChanges(), Times.Once); // Ensure SaveChanges was called + } + + [TestMethod] + public void DeleteTag_TagDoesNotExist_NothingHappens() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List()); // No tags in context + mockContext.Setup(c => c.Events).ReturnsDbSet(new List()); // No events associated with tags + + var tagService = new TagService(mockContext.Object); + + Assert.ThrowsException(() => tagService.DeleteTag(1)); + mockContext.Verify(c => c.SaveChanges(), Times.Never); // Ensure SaveChanges was never called + } + + [TestMethod] + public void DeleteTag_TagInUse_NothingHappens() + { + var tag = new Tag { Id = 1, Name = "TestTag" }; + var tags = new List { tag }; + var mockContext = new Mock(); + mockContext.Setup(c => c.Tags).ReturnsDbSet(tags); + mockContext.Setup(c => c.Events).ReturnsDbSet(new List + { + new Event { Tags = new List { tag } } + }); // Tag is associated with an event + + var tagService = new TagService(mockContext.Object); + + Assert.ThrowsException(() => tagService.DeleteTag(1)); + Assert.AreEqual(1, tags.Count); // Ensure the tag was not removed + mockContext.Verify(c => c.SaveChanges(), Times.Never); // Ensure SaveChanges was never called + } + + [TestMethod] + public void GetTagsByFilter_TagExist_TagsReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List { new Tag { Id = 1, Name = "TestTag" } }); + + var tagService = new TagService(mockContext.Object); + + var result = tagService.GetTagsByFilter(t => t.Name == "TestTag").ToList(); + + Assert.AreEqual(1, result.Count); + } + + [TestMethod] + public void GetTagsByFilter_TagsNotExist_TagsNotReturned() + { + var mockContext = new Mock(); + mockContext.Setup(c => c.Tags).ReturnsDbSet(new List { new Tag { Id = 1, Name = "TestTag" } }); + + var tagService = new TagService(mockContext.Object); + + var result = tagService.GetTagsByFilter(t => t.Name == "TestTag1").ToList(); + + Assert.AreEqual(0, result.Count); + } + + } +} diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/EventMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/EventMapper.cs new file mode 100644 index 00000000..0cf4cea7 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Mappers/EventMapper.cs @@ -0,0 +1,50 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; + +namespace ReasnAPI.Mappers +{ + public static class EventMapper + { + public static EventDto ToDto(this Event eventToMap) + { + var tags = eventToMap.Tags.ToDtoList(); + + var parameters = eventToMap.Parameters.ToDtoList(); + + return new EventDto + { + Name = eventToMap.Name, + AddressId = eventToMap.AddressId, + Description = eventToMap.Description, + OrganizerId = eventToMap.OrganizerId, + StartAt = eventToMap.StartAt, + EndAt = eventToMap.EndAt, + CreatedAt = eventToMap.CreatedAt, + UpdatedAt = eventToMap.UpdatedAt, + Slug = eventToMap.Slug, + Status = eventToMap.Status, + Tags = tags, + Parameters = parameters + }; + } + + public static List ToDtoList(this IEnumerable @event) + { + return @event.Select(ToDto).ToList(); + } + public static Event ToEntity(this EventDto eventDto) + { + return new Event + { + Name = eventDto.Name, + AddressId = eventDto.AddressId, + Description = eventDto.Description, + OrganizerId = eventDto.OrganizerId, + StartAt = eventDto.StartAt, + EndAt = eventDto.EndAt, + Status = eventDto.Status + }; + } + + } +} diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/ImageMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/ImageMapper.cs new file mode 100644 index 00000000..1e449b3d --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Mappers/ImageMapper.cs @@ -0,0 +1,34 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; + +namespace ReasnAPI.Mappers +{ + public static class ImageMapper + { + public static ImageDto ToDto(this Image image) + { + return new ImageDto + { + ImageData = image.ImageData, + ObjectId = image.ObjectId, + ObjectType = image.ObjectType + }; + } + + public static List ToDtoList(this IEnumerable images) + { + return images.Select(ToDto).ToList(); + } + + public static Image ToEntity(this ImageDto imageDto) + { + return new Image + { + ImageData = imageDto.ImageData, + ObjectId = imageDto.ObjectId, + ObjectType = imageDto.ObjectType + }; + } + + } +} diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/InterestMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/InterestMapper.cs index f6f75f72..e16863fe 100644 --- a/Server/ReasnAPI/ReasnAPI/Mappers/InterestMapper.cs +++ b/Server/ReasnAPI/ReasnAPI/Mappers/InterestMapper.cs @@ -1,28 +1,31 @@ using ReasnAPI.Models.Database; using ReasnAPI.Models.DTOs; -namespace ReasnAPI.Mappers; - -public static class InterestMapper +namespace ReasnAPI.Mappers { - public static InterestDto ToDto(this Interest interest) + public static class InterestMapper { - return new InterestDto + public static InterestDto ToDto(this Interest interest) { - Name = interest.Name - }; - } + return new InterestDto + { + Name = interest.Name + }; + } - public static List ToDtoList(this IEnumerable interests) - { - return interests.Select(ToDto).ToList(); - } + public static List ToDtoList(this IEnumerable interests) + { + return interests.Select(ToDto).ToList(); + } - public static Interest ToEntity(this InterestDto interestDto) - { - return new Interest + public static Interest ToEntity(this InterestDto interestDto) { - Name = interestDto.Name - }; + return new Interest + { + Name = interestDto.Name + }; + } } -} \ No newline at end of file + +} + diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/ParameterMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/ParameterMapper.cs new file mode 100644 index 00000000..76cdf96b --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Mappers/ParameterMapper.cs @@ -0,0 +1,36 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; + +namespace ReasnAPI.Mappers +{ + public static class ParameterMapper + { + public static ParameterDto ToDto(this Parameter parameter) + { + return new ParameterDto + { + Key = parameter.Key, + Value = parameter.Value + }; + } + public static List ToDtoList(this IEnumerable parameter) + { + return parameter.Select(ToDto).ToList(); + } + + public static List ToEntityList(this IEnumerable tags) + { + return tags.Select(ToEntity).ToList(); + } + + public static Parameter ToEntity(this ParameterDto parameterDto) + { + return new Parameter + { + Key = parameterDto.Key, + Value = parameterDto.Value + }; + } + + } +} diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/TagMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/TagMapper.cs new file mode 100644 index 00000000..c39d6508 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Mappers/TagMapper.cs @@ -0,0 +1,34 @@ +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; + +namespace ReasnAPI.Mappers +{ + public static class TagMapper + { + public static TagDto ToDto(this Tag tag) + { + return new TagDto + { + Name = tag.Name + }; + } + + public static List ToDtoList(this IEnumerable tags) + { + return tags.Select(ToDto).ToList(); + } + + public static List ToEntityList(this IEnumerable tags) + { + return tags.Select(ToEntity).ToList(); + } + + public static Tag ToEntity(this TagDto tagDto) + { + return new Tag + { + Name = tagDto.Name + }; + } + } +} diff --git a/Server/ReasnAPI/ReasnAPI/Models/DTOs/InterestDto.cs b/Server/ReasnAPI/ReasnAPI/Models/DTOs/InterestDto.cs index 87eb141e..b4225204 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/DTOs/InterestDto.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/DTOs/InterestDto.cs @@ -1,6 +1,8 @@ -namespace ReasnAPI.Models.DTOs +using System; + +namespace ReasnAPI.Models.DTOs { - public class InterestDto + public class InterestDto { public string Name { get; set; } = null!; } diff --git a/Server/ReasnAPI/ReasnAPI/Models/Database/Event.cs b/Server/ReasnAPI/ReasnAPI/Models/Database/Event.cs index 3f6dffdd..fef3be28 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/Database/Event.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/Database/Event.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using NpgsqlTypes; namespace ReasnAPI.Models.Database; diff --git a/Server/ReasnAPI/ReasnAPI/Models/Database/EventParameter.cs b/Server/ReasnAPI/ReasnAPI/Models/Database/EventParameter.cs deleted file mode 100644 index 32fa03fc..00000000 --- a/Server/ReasnAPI/ReasnAPI/Models/Database/EventParameter.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace ReasnAPI.Models.Database; - -public partial class EventParameter -{ - public int ParameterId { get; set; } - - public int EventId { get; set; } - - public virtual Event Event { get; set; } = null!; - - public virtual Parameter Parameter { get; set; } = null!; -} diff --git a/Server/ReasnAPI/ReasnAPI/Models/Database/EventTag.cs b/Server/ReasnAPI/ReasnAPI/Models/Database/EventTag.cs deleted file mode 100644 index e641f72d..00000000 --- a/Server/ReasnAPI/ReasnAPI/Models/Database/EventTag.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace ReasnAPI.Models.Database; - -public partial class EventTag -{ - public int EventId { get; set; } - - public int TagId { get; set; } - - public virtual Event Event { get; set; } = null!; - - public virtual Tag Tag { get; set; } = null!; -} diff --git a/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs b/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs index 9a2c04d1..aa85b86b 100644 --- a/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs +++ b/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs @@ -38,6 +38,7 @@ public ReasnContext(DbContextOptions options) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + if (!optionsBuilder.IsConfigured) { optionsBuilder.UseNpgsql("name=ConnectionStrings:DefaultValue"); @@ -47,7 +48,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder - .HasPostgresEnum("common", "event_status", new[] { "Completed", "In progress", "Approved", "Waiting for approval" }) + .HasPostgresEnum("common", "event_status", new[] { "Completed", "InProgress", "Approved", "WaitingForApproval" }) .HasPostgresEnum("common", "object_type", new[] { "Event", "User" }) .HasPostgresEnum("common", "participant_status", new[] { "Interested", "Participating" }) .HasPostgresEnum("users", "role", new[] { "User", "Organizer", "Admin" }); diff --git a/Server/ReasnAPI/ReasnAPI/Program.cs b/Server/ReasnAPI/ReasnAPI/Program.cs index 8b2051c9..b93353e9 100644 --- a/Server/ReasnAPI/ReasnAPI/Program.cs +++ b/Server/ReasnAPI/ReasnAPI/Program.cs @@ -1,29 +1,38 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; -using Npgsql; -using ReasnAPI.Models.Enums; using Serilog; using System; using System.Text.Json.Serialization; +using ReasnAPI.Models.DTOs; using ReasnAPI.Models.Database; using ReasnAPI.Services; +using Npgsql; +using ReasnAPI.Models.Enums; +using Microsoft.Extensions.Configuration; var builder = WebApplication.CreateSlimBuilder(args); -var connectionString = builder.Configuration.GetConnectionString("DefaultValue"); - -var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); -dataSourceBuilder.MapEnum("common.object_type"); -dataSourceBuilder.MapEnum("common.event_status"); -dataSourceBuilder.MapEnum("common.participant_status"); -dataSourceBuilder.MapEnum("users.role"); - -var dataSource = dataSourceBuilder.Build(); - -builder.Services.AddDbContext(options => +var connectionString = builder.Configuration.GetConnectionString("DefaultValue"); + +var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); +dataSourceBuilder.MapEnum("common.object_type"); +dataSourceBuilder.MapEnum("common.event_status"); +dataSourceBuilder.MapEnum("common.participant_status"); +dataSourceBuilder.MapEnum("users.role"); + +var dataSource = dataSourceBuilder.Build(); + +builder.Services.AddDbContext(options => options.UseNpgsql(dataSource)); +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 => @@ -61,4 +70,4 @@ app.MapGet("/", () => "Hello, World!"); -app.Run(); \ No newline at end of file +app.Run(); diff --git a/Server/ReasnAPI/ReasnAPI/Services/EventService.cs b/Server/ReasnAPI/ReasnAPI/Services/EventService.cs index 5f1be88a..3e3d3709 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/EventService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/EventService.cs @@ -1,16 +1,295 @@ -using ReasnAPI.Models.Database; - -namespace ReasnAPI.Services { - public class EventService (ReasnContext context) { - private readonly ReasnContext _context = context; - - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ - } -} +using Microsoft.EntityFrameworkCore; +using ReasnAPI.Exceptions; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using System.Linq.Expressions; +using System.Transactions; +using System.Text.RegularExpressions; +using ReasnAPI.Mappers; +using ReasnAPI.Models.Enums; + +namespace ReasnAPI.Services; +public class EventService(ReasnContext context, ParameterService parameterService, TagService tagService, CommentService commentService) +{ + + public EventDto CreateEvent(EventDto eventDto) + { + using (var scope = new TransactionScope()) + { + var createdTime = DateTime.UtcNow; + var newEvent = eventDto.ToEntity(); + newEvent.CreatedAt = createdTime; + newEvent.UpdatedAt = createdTime; + newEvent.Slug = CreateSlug(eventDto); + + + context.Events.Add(newEvent); + + context.SaveChanges(); + if (eventDto.Tags != null) + { + var newTags = eventDto.Tags.ToEntityList(); + tagService.AttatchTagsToEvent(newTags, newEvent); + } + + if (eventDto.Parameters != null) + { + var newParameters = eventDto.Parameters.ToEntityList(); + parameterService.AttachParametersToEvent(newParameters, newEvent); + } + + context.SaveChanges(); + scope.Complete(); + eventDto.Slug = newEvent.Slug; + } + + return eventDto; + } + + public EventDto UpdateEvent(int eventId, EventDto eventDto) + { + using (var scope = new TransactionScope()) + { + var eventToUpdate = context.Events.Include(e => e.Tags).Include(e => e.Parameters).FirstOrDefault(e => e.Id == eventId); + + if (eventToUpdate is null) + { + throw new NotFoundException("Event not found"); + } + if (eventToUpdate.Name != eventDto.Name) + { + eventToUpdate.Slug = CreateSlug(eventDto); + } + + eventToUpdate.Name = eventDto.Name; + eventToUpdate.AddressId = eventDto.AddressId; + eventToUpdate.Description = eventDto.Description; + eventToUpdate.OrganizerId = eventDto.OrganizerId; + eventToUpdate.StartAt = eventDto.StartAt; + eventToUpdate.EndAt = eventDto.EndAt; + eventToUpdate.UpdatedAt = DateTime.UtcNow; + eventToUpdate.Status = eventDto.Status; + + if (eventDto.Tags == null) + { + DetachTagsFromEvent(eventToUpdate.Tags.ToList(), eventToUpdate); + } + else + { + var newTags = eventDto.Tags.ToEntityList(); + var tagsToRemove = eventToUpdate.Tags + .Where(existingTag => newTags.All(newTag => newTag.Name != existingTag.Name)) + .ToList(); + DetachTagsFromEvent(tagsToRemove, eventToUpdate); + tagService.AttatchTagsToEvent(newTags, eventToUpdate); + } + + if (eventDto.Parameters == null) + { + DetachParametersFromEvent(eventToUpdate.Parameters.ToList(), eventToUpdate); + } + else + { + var newParameters = eventDto.Parameters.ToEntityList(); + var paramsToRemove = eventToUpdate.Parameters + .Where(existingParam => newParameters.All(newParam => newParam.Key != existingParam.Key || newParam.Value != existingParam.Value)) + .ToList(); + DetachParametersFromEvent(paramsToRemove, eventToUpdate); + parameterService.AttachParametersToEvent(newParameters, eventToUpdate); + } + + context.Events.Update(eventToUpdate); + + context.SaveChanges(); + scope.Complete(); + } + + return eventDto; + } + + private void DetachTagsFromEvent(List tagsToRemove, Event eventToUpdate) + { + tagsToRemove.ForEach(tag => eventToUpdate.Tags.Remove(tag)); + context.SaveChanges(); + + tagService.RemoveTagsNotInAnyEvent(); + } + + private void DetachParametersFromEvent(List parametersToRemove, Event eventToUpdate) + { + parametersToRemove.ForEach(param => eventToUpdate.Parameters.Remove(param)); + context.SaveChanges(); + + parameterService.RemoveParametersNotInAnyEvent(); + } + + public void DeleteEvent(int eventId) + { + using (var scope = new TransactionScope()) + { + var eventToDelete = context.Events.Include(e => e.Tags).Include(e => e.Parameters).Include(e => e.Participants).Include(e => e.Comments).FirstOrDefault(e => e.Id == eventId); + + if (eventToDelete is null) + { + throw new NotFoundException("Event not found"); + } + + var parametersToRemove = eventToDelete.Parameters.ToList(); + var tagsToRemove = eventToDelete.Tags.ToList(); + + DetachParametersFromEvent(parametersToRemove, eventToDelete); + DetachTagsFromEvent(tagsToRemove, eventToDelete); + + RemoveCommentsFromEventCollection(eventToDelete); + RemoveParticipantsFromEventCollection(eventToDelete); + + context.Events.Remove(eventToDelete); + context.SaveChanges(); + scope.Complete(); + } + } + + private void RemoveCommentsFromEventCollection(Event eventToDelete) + { + context.Comments.RemoveRange(eventToDelete.Comments); + } + + private void RemoveParticipantsFromEventCollection(Event eventToDelete) + { + context.Participants.RemoveRange(eventToDelete.Participants); + } + + public EventDto GetEventById(int eventId) + { + var eventToReturn = context.Events.Include(e => e.Tags).Include(e => e.Parameters).FirstOrDefault(e => e.Id == eventId); + if (eventToReturn is null) + { + throw new NotFoundException("Event not found"); + } + + var eventDto = eventToReturn.ToDto(); + + return eventDto; + } + + public Event GetEventBySlug(string slug) + { + var eventToReturn = context.Events.Include(e => e.Tags).Include(e => e.Parameters).FirstOrDefault(e => e.Slug == slug); + if (eventToReturn is null) + { + throw new NotFoundException("Event not found"); + } + + + return eventToReturn; + } + + public int GetEventParticipantsCountBySlugAndStatus(string slug, ParticipantStatus status) + { + var eventToReturn = context.Events.Include(e => e.Participants).FirstOrDefault(e => e.Slug == slug); + if (eventToReturn is null) + { + throw new NotFoundException("Event not found"); + } + + return eventToReturn.Participants.Count(p => p.Status == status); + } + + public IEnumerable GetEventParticipantsBySlugAndStatus(string slug, ParticipantStatus status) + { + var eventToReturn = context.Events.Include(e => e.Participants).FirstOrDefault(e => e.Slug == slug); + if (eventToReturn is null) + { + throw new NotFoundException("Event not found"); + } + + var participantDtos = eventToReturn.Participants + .Where(p => p.Status == status) + .Select(p => p.ToDto()); + + return participantDtos; + } + + public IEnumerable GetEventCommentsBySlug(string slug) + { + var eventToReturn = context.Events.Include(e => e.Comments) + .FirstOrDefault(e => e.Slug == slug); + if (eventToReturn is null) + { + throw new NotFoundException("Event not found"); + } + + var commentDtos = eventToReturn.Comments.Select(p => p.ToDto()); + + return commentDtos; + } + + public void AddEventComment(CommentDto commentDto, string slug) + { + commentDto = commentService.CreateComment(commentDto); + var relatedEvent = GetEventBySlug(slug); + relatedEvent.Comments.Add(commentDto.ToEntity()); + context.SaveChanges(); + } + + public IEnumerable GetEventsByFilter(Expression> filter) + { + var events = context.Events.Include(e => e.Parameters).Include(e => e.Tags).Where(filter).ToList(); + var eventDtos = events.ToDtoList(); + return eventDtos; + } + + public IEnumerable GetAllEvents() + { + var events = context.Events.Include(e => e.Parameters).Include(e => e.Tags).ToList(); + + var eventDtos = events.ToDtoList(); + return eventDtos; + } + public IEnumerable GetUserEvents(string username) + { + var user = context.Users.FirstOrDefault(u => u.Username == username); + + if (user is null) + { + throw new NotFoundException("User not found"); + } + + var userEvents = context.Participants.Include(p => p.Event).Where(p => p.UserId == user.Id).Select(p => p.Event); + return userEvents.ToDtoList().AsEnumerable(); + } + + private string CreateSlug(EventDto eventDto) + { + var baseSlug = Regex.Replace(eventDto.Name.ToLower().Trim(), @"[^a-z0-9\s-]", "") + .Replace(" ", "-") + .Replace("--", "-"); + + var pattern = $"^{Regex.Escape(baseSlug)}(-\\d+)?$"; + + var existingSlugs = context.Events + .Select(e => e.Slug) + .AsNoTracking() + .AsEnumerable() + .Where(slug => Regex.IsMatch(slug, pattern)); + + var highestNumber = existingSlugs + .Select(slug => Regex.Match(slug, $"^{Regex.Escape(baseSlug)}-(\\d+)$")) + .Where(match => match.Success) + .Select(match => int.Parse(match.Groups[1].Value)) + .DefaultIfEmpty(0) + .Max(); + + var counter = highestNumber + 1; + var finalSlug = $"{baseSlug}-{counter}"; + + if (finalSlug.Length > 128) + { + finalSlug = $"{baseSlug[..(128 - counter.ToString().Length - 1)]}-{counter}"; + } + + return finalSlug; + + } + +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Services/ImageService.cs b/Server/ReasnAPI/ReasnAPI/Services/ImageService.cs index e077a215..59d688d4 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/ImageService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/ImageService.cs @@ -1,16 +1,224 @@ using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using System.Linq.Expressions; +using ReasnAPI.Exceptions; +using ReasnAPI.Models.Enums; +using ReasnAPI.Mappers; + + +namespace ReasnAPI.Services; +public class ImageService(ReasnContext context) +{ + public IEnumerable CreateImages(List imageDtos) + { + if (!imageDtos.Any()) + { + throw new ArgumentException("No images provided"); + } + + var newImages = new List(); + + if (imageDtos[0].ObjectType == ObjectType.User && imageDtos.Count > 1) + { + throw new ArgumentException("For User type, only one image can be created"); + } + + foreach (var imageDto in imageDtos) + { + var image = context.Images.FirstOrDefault(r => r.ObjectId == imageDto.ObjectId && r.ObjectType == imageDto.ObjectType && r.ImageData == imageDto.ImageData); + if (image is not null) + { + continue; + } + + var newImage = imageDto.ToEntity(); + + newImages.Add(newImage); + } + + if (newImages.Any()) + { + var objectType = newImages[0].ObjectType; + + if (objectType == ObjectType.User && newImages.Count == 1) + { + context.Images.Add(newImages[0]); + } + else if (objectType == ObjectType.Event) + { + context.Images.AddRange(newImages); + } + + context.SaveChanges(); + } + + return imageDtos.AsEnumerable(); + } + + public void UpdateImagesForEvent(int objectId, List imageDtos) + { + if (!imageDtos.Any()) + { + throw new ArgumentException("No images provided"); + } + + var objectType = imageDtos[0].ObjectType; + + if (objectType == ObjectType.User) + { + throw new ArgumentException("For User type, use UpdateImage"); + } + else if (objectType == ObjectType.Event) + { + var images = context.Images.Where(r => r.ObjectId == objectId && r.ObjectType == ObjectType.Event).ToList(); + + foreach (var image in images) + { + if (!imageDtos.Any(dto => dto.ImageData == image.ImageData)) + { + context.Images.Remove(image); + } + } + + var newImages = new List(); + foreach (var imageDto in imageDtos) + { + if (!images.Any(img => img.ImageData == imageDto.ImageData)) + { + var newImage = imageDto.ToEntity(); + newImages.Add(newImage); + } + } + context.Images.AddRange(newImages); + } + + context.SaveChanges(); + } + + public void UpdateImageForUser(int userId, ImageDto imageDto) + { + if (imageDto is null) + { + throw new ArgumentException("No image provided"); + } + + var image = context.Images.FirstOrDefault(i => i.ObjectType == ObjectType.User && i.ObjectId == userId); + if (image is null) + { + throw new NotFoundException("Image not found"); + } + + image.ImageData = imageDto.ImageData; + context.Images.Update(image); + context.SaveChanges(); + + } -namespace ReasnAPI.Services { - public class ImageService (ReasnContext context) { - private readonly ReasnContext _context = context; + public void DeleteImageById(int id) + { + var image = context.Images.FirstOrDefault(r => r.Id == id); + if (image is null) + { + throw new NotFoundException("Image not found"); + } + + context.Images.Remove(image); + context.SaveChanges(); + } + + public void DeleteImageRelatedToEvent(int id, string slug) + { + var relatedEvent = context.Events.FirstOrDefault(r => r.Slug == slug); + if (relatedEvent is null) + { + throw new NotFoundException("Event not found"); + } + + var image = context.Images.FirstOrDefault(r => r.Id == id); + if (image is null) + { + throw new NotFoundException("Image not found"); + } + + if (image.ObjectId != relatedEvent.Id || image.ObjectType != ObjectType.Event) + { + throw new NotFoundException("This image is not related with this event"); + } + + context.Images.Remove(image); + context.SaveChanges(); + } - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ + public void DeleteImageByObjectIdAndType(int objectId, ObjectType objectType) + { + var images = context.Images.Where(r => r.ObjectId == objectId && r.ObjectType == objectType).ToList(); + if (!images.Any()) + { + throw new NotFoundException("Images not found"); + } + + context.Images.RemoveRange(images); + context.SaveChanges(); } -} + + public ImageDto GetImageById(int id) + { + var image = context.Images.Find(id); + if (image is null) + { + throw new NotFoundException("Image not found"); + } + + var imageDto = image.ToDto(); + + return imageDto; + } + + public IEnumerable GetImagesByUserId(int userId) + { + var images = context.Images + .Where(image => image.ObjectId == userId && image.ObjectType == ObjectType.User) + .ToList(); + + if (!images.Any()) + { + throw new NotFoundException("Images not found"); + } + + var imageDtos = images.ToDtoList().AsEnumerable(); + + return imageDtos; + } + + public IEnumerable GetAllImages() + { + return context.Images + .ToDtoList() + .AsEnumerable(); + } + + public IEnumerable GetImagesByFilter(Expression> filter) + { + return context.Images + .Where(filter) + .ToDtoList() + .AsEnumerable(); + } + + public IEnumerable GetImagesByEventId(int eventId) + { + var images = context.Images + .Where(image => image.ObjectType == ObjectType.Event && image.ObjectId == eventId) + .ToList(); + + if (!images.Any()) + { + throw new NotFoundException("Images not found"); + } + + var imageDtos = images.ToDtoList().AsEnumerable(); + + return imageDtos; + } + +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Services/InterestService.cs b/Server/ReasnAPI/ReasnAPI/Services/InterestService.cs new file mode 100644 index 00000000..9d4bd7d2 --- /dev/null +++ b/Server/ReasnAPI/ReasnAPI/Services/InterestService.cs @@ -0,0 +1,100 @@ +using ReasnAPI.Exceptions; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using System.Linq.Expressions; +using ReasnAPI.Mappers; + +namespace ReasnAPI.Services; + +public class InterestService(ReasnContext context) +{ + public InterestDto CreateInterest(InterestDto interestDto) + { + var interest = context.Interests.FirstOrDefault(r => r.Name == interestDto.Name); + if (interest is not null) + { + throw new BadRequestException("Interest already exists"); + } + + var newInterest = interestDto.ToEntity(); + + context.Interests.Add(newInterest); + context.SaveChanges(); + return interestDto; + } + + public InterestDto UpdateInterest(int interestId, InterestDto interestDto) + { + var interest = context.Interests.FirstOrDefault(r => r.Id == interestId); + if (interest is null) + { + throw new NotFoundException("Interest not found"); + } + + interest.Name = interestDto.Name; + + context.Interests.Update(interest); + context.SaveChanges(); + return interestDto; + } + + public void DeleteInterest(int id) + { + var interest = context.Interests.FirstOrDefault(r => r.Id == id); + if (interest is null) + { + throw new NotFoundException("Interest not found"); + } + + var userInterest = context.UserInterests.FirstOrDefault(r => r.InterestId == id); + if (userInterest is not null) + { + throw new BadRequestException("Interest is in use"); + } + + context.Interests.Remove(interest); + context.SaveChanges(); + } + + public void ForceDeleteInterest(int id) + { + var interest = context.Interests.FirstOrDefault(r => r.Id == id); + if (interest is null) + { + throw new NotFoundException("Interest not found"); + } + + var userInterests = context.UserInterests.Where(r => r.InterestId == id).ToList(); + context.UserInterests.RemoveRange(userInterests); + + context.Interests.Remove(interest); + context.SaveChanges(); + } + + public InterestDto GetInterestById(int interestId) + { + var interest = context.Interests.Find(interestId); + if (interest is null) + { + throw new NotFoundException("Interest not found"); + } + + return interest.ToDto(); + } + + public IEnumerable GetAllInterests() + { + return context.Interests + .ToDtoList() + .AsEnumerable(); + } + + public IEnumerable GetInterestsByFilter(Expression> filter) + { + return context.Interests + .Where(filter) + .ToDtoList() + .AsEnumerable(); + } + +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Services/IntrestService.cs b/Server/ReasnAPI/ReasnAPI/Services/IntrestService.cs deleted file mode 100644 index 5f2087fa..00000000 --- a/Server/ReasnAPI/ReasnAPI/Services/IntrestService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ReasnAPI.Models.Database; - -namespace ReasnAPI.Services { - public class IntrestService (ReasnContext context) { - private readonly ReasnContext _context = context; - - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ - } -} diff --git a/Server/ReasnAPI/ReasnAPI/Services/ParameterService.cs b/Server/ReasnAPI/ReasnAPI/Services/ParameterService.cs index 9c178c2f..f38c8534 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/ParameterService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/ParameterService.cs @@ -1,16 +1,157 @@ -using ReasnAPI.Models.Database; - -namespace ReasnAPI.Services { - public class ParameterService (ReasnContext context){ - private readonly ReasnContext _context = context; - - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ - } -} +using Microsoft.EntityFrameworkCore; +using ReasnAPI.Exceptions; +using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using System.Linq.Expressions; +using ReasnAPI.Mappers; + +namespace ReasnAPI.Services; +public class ParameterService(ReasnContext context) +{ + public ParameterDto CreateParameter(ParameterDto parameterDto) + { + var parameter = context.Parameters.FirstOrDefault(r => r.Key == parameterDto.Key && r.Value == parameterDto.Value); + if (parameter is not null) + { + throw new BadRequestException("Parameter already exists"); + } + + var newParameter = parameterDto.ToEntity(); + context.Parameters.Add(newParameter); + context.SaveChanges(); + return parameterDto; + } + + public ParameterDto UpdateParameter(int parameterId, ParameterDto parameterDto) + { + var parameter = context.Parameters.FirstOrDefault(r => r.Id == parameterId); + + if (parameter is null) + { + throw new NotFoundException("Parameter not found"); + } + + var parameters = context.Events.Include(p => p.Parameters); + + var parameterCheck = parameters.FirstOrDefault(r => r.Parameters.Any(p => p.Id == parameterId)); + + if (parameterCheck is not null) + { + throw new BadRequestException("Parameter is associated with an event"); + } + + parameter.Key = parameterDto.Key; + parameter.Value = parameterDto.Value; + context.Parameters.Update(parameter); + context.SaveChanges(); + return parameterDto; + } + + public void DeleteParameter(int parameterId) + { + var parameter = context.Parameters.FirstOrDefault(r => r.Id == parameterId); + + if (parameter == null) + { + throw new NotFoundException("Parameter not found"); + } + + var eventsWithParameters = context.Events + .Include(e => e.Parameters) + .ToList(); + + var parameterCheck = eventsWithParameters + .Any(e => e.Parameters.Any(p => p.Id == parameterId)); + + if (parameterCheck) + { + throw new BadRequestException("Parameter is associated with an event"); + } + + context.Parameters.Remove(parameter); + context.SaveChanges(); + } + + public void ForceDeleteParameter(ParameterDto parameterDto) + { + var parameter = context.Parameters.FirstOrDefault(r => r.Key == parameterDto.Key && r.Value == parameterDto.Value); + if (parameter is null) + { + throw new NotFoundException("Parameter not found"); + } + + var eventsWithParameter = context.Events + .Where(e => e.Parameters.Any(p => p.Key == parameterDto.Key && p.Value == parameterDto.Value)) + .Include(e => e.Parameters) + .ToList(); + + foreach (var eventWithParameter in eventsWithParameter) + { + eventWithParameter.Parameters.Remove(parameter); + } + + context.Parameters.Remove(parameter); + context.SaveChanges(); + } + + public void RemoveParametersNotInAnyEvent() + { + var parametersNotInAnyEvent = context.Parameters + .Where(p => !context.Events.Any(e => e.Parameters.Contains(p))) + .ToList(); + + context.Parameters.RemoveRange(parametersNotInAnyEvent); + context.SaveChanges(); + } + + public void AttachParametersToEvent(List parametersToAdd, Event eventToUpdate) + { + var parameterKeyValuePairsToAdd = parametersToAdd.Select(p => new { p.Key, p.Value }).ToList(); + + var keysToAdd = parametersToAdd.Select(p => p.Key).Distinct().ToList(); + var valuesToAdd = parametersToAdd.Select(p => p.Value).Distinct().ToList(); + + var parametersFromDb = context.Parameters + .Where(param => keysToAdd.Contains(param.Key) && valuesToAdd.Contains(param.Value)) + .ToList(); + + parametersFromDb.ForEach(eventToUpdate.Parameters.Add); + + var newParametersToAdd = parametersToAdd.Where(paramToAdd => + !parametersFromDb.Any(existingParam => existingParam.Key == paramToAdd.Key && existingParam.Value == paramToAdd.Value)) + .ToList(); + + newParametersToAdd.ForEach(eventToUpdate.Parameters.Add); + + context.SaveChanges(); + } + + public ParameterDto GetParameterById(int parameterId) + { + var parameter = context.Parameters.Find(parameterId); + if (parameter is null) + { + throw new NotFoundException("Parameter not found"); + } + + var parameterDto = parameter.ToDto(); + return parameterDto; + } + + public IEnumerable GetAllParameters() + { + return context.Parameters + .ToDtoList() + .AsEnumerable(); + } + + public IEnumerable GetParametersByFilter(Expression> filter) + { + return context.Parameters + .Where(filter) + .ToDtoList() + .AsEnumerable(); + } + + +} \ No newline at end of file diff --git a/Server/ReasnAPI/ReasnAPI/Services/TagService.cs b/Server/ReasnAPI/ReasnAPI/Services/TagService.cs index a79b01c9..65f27409 100644 --- a/Server/ReasnAPI/ReasnAPI/Services/TagService.cs +++ b/Server/ReasnAPI/ReasnAPI/Services/TagService.cs @@ -1,16 +1,141 @@ using ReasnAPI.Models.Database; +using ReasnAPI.Models.DTOs; +using System.Linq.Expressions; +using System.Transactions; +using Microsoft.EntityFrameworkCore; +using ReasnAPI.Exceptions; +using ReasnAPI.Mappers; -namespace ReasnAPI.Services { - public class TagService (ReasnContext context) { - private readonly ReasnContext _context = context; +namespace ReasnAPI.Services; +public class TagService(ReasnContext context) +{ + public TagDto CreateTag(TagDto tagDto) + { + var tag = context.Tags.FirstOrDefault(r => r.Name == tagDto.Name); + if (tag is not null) + { + throw new BadRequestException("Tag already exists"); + } - /* TODO: Create following functions for this class - * create - * update - * delete - * get by ID - * get list by filter - * get all - */ + context.Tags.Add(tagDto.ToEntity()); + context.SaveChanges(); + return tagDto; } -} + + public TagDto UpdateTag(int tagId, TagDto tagDto) + { + var tag = context.Tags.FirstOrDefault(r => r.Id == tagId); + + if (tag is null) + { + throw new NotFoundException("Tag not found"); + } + + tag.Name = tagDto.Name; + context.Tags.Update(tag); + context.SaveChanges(); + + return tagDto; + } + + public void AttatchTagsToEvent(List tagsToAdd, Event eventToUpdate) + { + + var tagNamesToAdd = tagsToAdd.Select(t => t.Name).ToList(); + + var tagsFromDb = context.Tags.Where(tag => tagNamesToAdd.Contains(tag.Name)).ToList(); + + tagsFromDb.ForEach(eventToUpdate.Tags.Add); + + var newTagsToAdd = tagsToAdd.Where(t => tagsFromDb.All(dbTag => dbTag.Name != t.Name)).ToList(); + + newTagsToAdd.ForEach(eventToUpdate.Tags.Add); + context.SaveChanges(); + } + + public void DeleteTag(int tagId) + { + var tag = context.Tags.FirstOrDefault(r => r.Id == tagId); + + if (tag is null) + { + throw new NotFoundException("Tag not found"); + } + + var eventsWithTags = context.Events.Include(e => e.Tags).ToList(); + + var isTagAssociatedWithEvent = eventsWithTags.Any(e => e.Tags.Any(t => t.Id == tagId)); + + if (isTagAssociatedWithEvent) + { + throw new BadRequestException("Tag is associated with an event"); + } + + context.Tags.Remove(tag); + context.SaveChanges(); + } + + public void ForceDeleteTag(TagDto tagDto) + { + if (tagDto is null) + { + throw new ArgumentException("No tag provided"); + } + + var tag = context.Tags.FirstOrDefault(r => r.Name == tagDto.Name); + if (tag is null) + { + throw new NotFoundException("Tag not found"); + } + + var eventsWithTags = context.Events + .Where(e => e.Tags.Any(p => p.Name == tagDto.Name)) + .Include(e => e.Tags) + .ToList(); + + foreach (var eventWithTag in eventsWithTags) + { + eventWithTag.Tags.Remove(tag); + } + + + context.Tags.Remove(tag); + context.SaveChanges(); + } + + public void RemoveTagsNotInAnyEvent() + { + var tagsNotInAnyEvent = context.Tags + .Where(t => !context.Events.Any(e => e.Tags.Contains(t))) + .ToList(); + + context.Tags.RemoveRange(tagsNotInAnyEvent); + context.SaveChanges(); + } + + public TagDto GetTagById(int tagId) + { + var tag = context.Tags.Find(tagId); + if (tag is null) + { + throw new NotFoundException("Tag not found"); + } + + return tag.ToDto(); + } + + public IEnumerable GetAllTags() + { + return context.Tags + .ToDtoList() + .AsEnumerable(); + } + + public IEnumerable GetTagsByFilter(Expression> filter) + { + return context.Tags + .Where(filter) + .ToDtoList() + .AsEnumerable(); + } +} \ No newline at end of file