diff --git a/Database/init-db.sql b/Database/init-db.sql
index 6e761f9c..5afb8ebc 100644
--- a/Database/init-db.sql
+++ b/Database/init-db.sql
@@ -3,7 +3,7 @@ CREATE SCHEMA users;
CREATE SCHEMA common;
CREATE TYPE common.participant_status AS ENUM ('Interested', 'Participating');
-CREATE TYPE common.event_status AS ENUM ('Completed', 'In progress', 'Approved', 'Waiting for approval');
+CREATE TYPE common.event_status AS ENUM ('Completed', 'InProgress', 'Approved', 'WaitingForApproval');
CREATE TYPE users.role AS ENUM ('User', 'Organizer', 'Admin');
CREATE TYPE common.object_type AS ENUM ('Event', 'User');
diff --git a/Database/init-dev-data.sql b/Database/init-dev-data.sql
index 818e37d3..a2a85a21 100644
--- a/Database/init-dev-data.sql
+++ b/Database/init-dev-data.sql
@@ -26,14 +26,14 @@ SELECT setval('users.user_id_seq', (SELECT MAX(id) FROM users.user));
INSERT INTO events.event ("id", "name", "address_id", "description", "organizer_id", "start_at", "end_at", "created_at", "updated_at", "slug", "status") VALUES
(1, 'Tech Conference', 1, 'Annual tech conference', 1, '2023-10-01 09:00:00', '2023-10-02 17:00:00', '2023-09-01 08:00:00', '2023-09-01 08:00:00', 'tech-conference', 'Approved'),
-(2, 'Health Symposium', 2, 'Health and wellness symposium', 2, '2023-11-05 09:00:00', '2023-11-06 17:00:00', '2023-10-05 08:00:00', '2023-10-05 08:00:00', 'health-symposium', 'In progress'),
-(3, 'Koncert Rockowy', 3, 'Występ ulubionych zespołów rockowych', 2, CURRENT_TIMESTAMP - '1 day'::INTERVAL, CURRENT_TIMESTAMP + '4 hours'::INTERVAL, '2023-09-01 08:00:00', '2023-09-01 08:00:00', 'koncert-rockowy', 'Waiting for approval'),
+(2, 'Health Symposium', 2, 'Health and wellness symposium', 2, '2023-11-05 09:00:00', '2023-11-06 17:00:00', '2023-10-05 08:00:00', '2023-10-05 08:00:00', 'health-symposium', 'InProgress'),
+(3, 'Koncert Rockowy', 3, 'Występ ulubionych zespołów rockowych', 2, CURRENT_TIMESTAMP - '1 day'::INTERVAL, CURRENT_TIMESTAMP + '4 hours'::INTERVAL, '2023-09-01 08:00:00', '2023-09-01 08:00:00', 'koncert-rockowy', 'WaitingForApproval'),
(4, 'Konferencja IT', 4, 'Coroczna konferencja technologiczna', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + '3 hours'::INTERVAL, '2023-10-02 17:00:00', '2023-10-02 18:00:00', 'konferencja-it', 'Completed'),
-(5, 'Mecz Piłki Nożnej', 5, 'Mecz drużynowej rywalizacji w piłce nożnej', 3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + '3 hours'::INTERVAL, '2022-12-01 08:00:00', '2023-09-01 08:30:00', 'mecz-pilki-noznej', 'In progress'),
-(6, 'Festiwal Elektroniczny', 6, 'Największe hity muzyki elektronicznej', 4, CURRENT_TIMESTAMP - '4 day'::INTERVAL, CURRENT_TIMESTAMP - '2 days'::INTERVAL, '2023-09-01 08:00:00', '2023-09-02 08:00:00', 'festiwal-elektroniczny', 'Waiting for approval'),
+(5, 'Mecz Piłki Nożnej', 5, 'Mecz drużynowej rywalizacji w piłce nożnej', 3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + '3 hours'::INTERVAL, '2022-12-01 08:00:00', '2023-09-01 08:30:00', 'mecz-pilki-noznej', 'InProgress'),
+(6, 'Festiwal Elektroniczny', 6, 'Największe hity muzyki elektronicznej', 4, CURRENT_TIMESTAMP - '4 day'::INTERVAL, CURRENT_TIMESTAMP - '2 days'::INTERVAL, '2023-09-01 08:00:00', '2023-09-02 08:00:00', 'festiwal-elektroniczny', 'WaitingForApproval'),
(7, 'Koncert Hip-Hopowy', 7, 'Najnowsze hity hip-hopu w wykonaniu gwiazd', 6, CURRENT_TIMESTAMP + '2 days'::INTERVAL, CURRENT_TIMESTAMP + '2 days'::INTERVAL + '4 hours'::INTERVAL, '2023-10-08 17:00:00', '2023-11-01 09:19:22', 'koncert-hip-hopowy', 'Completed'),
-(8, 'Wieczór Jazzowy', 8, 'Relaksujące dźwięki jazzu w kameralnej atmosferze', 8, CURRENT_TIMESTAMP + '1 day'::INTERVAL, CURRENT_TIMESTAMP + '1 day'::INTERVAL + '5 hours'::INTERVAL, '2023-09-01 08:00:00', '2023-09-01 08:00:00', 'wieczor-jazzowy', 'In progress'),
-(9, 'Koncert Klasyczny', 9, 'Muzyka klasyczna w wykonaniu renomowanych artystów', 10, CURRENT_TIMESTAMP + '1 day'::INTERVAL, CURRENT_TIMESTAMP + '1 day'::INTERVAL + '3 hours'::INTERVAL, '2023-01-01 15:00:00', '2023-02-01 08:00:00', 'koncert-klasyczny', 'Waiting for approval'),
+(8, 'Wieczór Jazzowy', 8, 'Relaksujące dźwięki jazzu w kameralnej atmosferze', 8, CURRENT_TIMESTAMP + '1 day'::INTERVAL, CURRENT_TIMESTAMP + '1 day'::INTERVAL + '5 hours'::INTERVAL, '2023-09-01 08:00:00', '2023-09-01 08:00:00', 'wieczor-jazzowy', 'InProgress'),
+(9, 'Koncert Klasyczny', 9, 'Muzyka klasyczna w wykonaniu renomowanych artystów', 10, CURRENT_TIMESTAMP + '1 day'::INTERVAL, CURRENT_TIMESTAMP + '1 day'::INTERVAL + '3 hours'::INTERVAL, '2023-01-01 15:00:00', '2023-02-01 08:00:00', 'koncert-klasyczny', 'WaitingForApproval'),
(10, 'Turniej w League of Legends', 10, 'Turniej LAN dla miłośnikow esportu i gry League of Legends', 10, CURRENT_TIMESTAMP - '1 day'::INTERVAL, CURRENT_TIMESTAMP - '3 hours'::INTERVAL, '2023-01-01 15:00:00', '2023-02-01 08:00:00', 'turniej-lol', 'Completed');
SELECT setval('events.event_id_seq', (SELECT MAX(id) FROM events.event));
diff --git a/Server/ReasnAPI/ReasnAPI.Tests/ReasnAPI.Tests.csproj b/Server/ReasnAPI/ReasnAPI.Tests/ReasnAPI.Tests.csproj
index 1b46a3ed..3f9b022c 100644
--- a/Server/ReasnAPI/ReasnAPI.Tests/ReasnAPI.Tests.csproj
+++ b/Server/ReasnAPI/ReasnAPI.Tests/ReasnAPI.Tests.csproj
@@ -11,12 +11,18 @@
+
+
+
+
+
+
diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/AddressServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/AddressServiceTests.cs
new file mode 100644
index 00000000..2707c98d
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/AddressServiceTests.cs
@@ -0,0 +1,294 @@
+using Moq;
+using Moq.EntityFrameworkCore;
+using ReasnAPI.Exceptions;
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+using ReasnAPI.Services;
+
+namespace ReasnAPI.Tests.Services;
+
+[TestClass]
+public class AddressServiceTests
+{
+ [TestMethod]
+ public void GetAddressById_AddressExists_AddressReturned()
+ {
+ var mockContext = new Mock();
+
+ var address = new Address
+ {
+ Id = 1,
+ City = "City",
+ Country = "Country",
+ State = "State",
+ Street = "Street",
+ ZipCode = "ZipCode"
+ };
+
+ var fakeAddress = new FakeDbSet { address };
+
+ mockContext.Setup(c => c.Addresses).Returns(fakeAddress);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ var result = addressService.GetAddressById(1);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("City", result.City);
+ Assert.AreEqual("Country", result.Country);
+ Assert.AreEqual("State", result.State);
+ Assert.AreEqual("Street", result.Street);
+ Assert.AreEqual("ZipCode", result.ZipCode);
+ }
+
+ [TestMethod]
+ public void GetAddressById_AddressDoesNotExist_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ Assert.ThrowsException(() => addressService.GetAddressById(1));
+ }
+
+ [TestMethod]
+ public void GetAllAddresses_AddressesExist_AddressesReturned()
+ {
+ var mockContext = new Mock();
+
+ var address1 = new Address
+ {
+ Id = 1,
+ City = "City",
+ Country = "Country",
+ State = "State",
+ Street = "Street",
+ ZipCode = "ZipCode"
+ };
+
+ var address2 = new Address
+ {
+ Id = 2,
+ City = "City",
+ Country = "Country",
+ State = "State",
+ Street = "Street",
+ ZipCode = "ZipCode"
+ };
+
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([address1, address2]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ var result = addressService.GetAllAddresses();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(2, result.Count());
+ }
+
+ [TestMethod]
+ public void GetAllAddresses_NoAddresses_EmptyListReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ var result = addressService.GetAllAddresses();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(0, result.Count());
+ }
+
+ [TestMethod]
+ public void GetAddressesByFilter_AddressesExist_AddressesReturned()
+ {
+ var mockContext = new Mock();
+
+ var address1 = new Address
+ {
+ Id = 1,
+ City = "City",
+ Country = "Country",
+ State = "State",
+ Street = "Street",
+ ZipCode = "ZipCode"
+ };
+
+ var address2 = new Address
+ {
+ Id = 2,
+ City = "City",
+ Country = "Country",
+ State = "State",
+ Street = "Street",
+ ZipCode = "ZipCode"
+ };
+
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([address1, address2]);
+ var addressService = new AddressService(mockContext.Object);
+
+ var result = addressService.GetAddressesByFilter(r => r.Id == 1).ToList();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(1, result.Count);
+ }
+
+ [TestMethod]
+ public void GetAddressesByFilter_NoAddresses_EmptyListReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ var result = addressService.GetAddressesByFilter(r => r.Id == 1);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(0, result.Count());
+ }
+
+ [TestMethod]
+ public void CreateAddress_AddressCreated_AddressReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ var addressDto = new AddressDto
+ {
+ City = "City",
+ Country = "Country",
+ State = "State",
+ Street = "Street",
+ ZipCode = "ZipCode"
+ };
+
+ var result = addressService.CreateAddress(addressDto);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("City", result.City);
+ Assert.AreEqual("Country", result.Country);
+ Assert.AreEqual("State", result.State);
+ Assert.AreEqual("Street", result.Street);
+ Assert.AreEqual("ZipCode", result.ZipCode);
+ }
+
+ [TestMethod]
+ public void CreateAddress_AddressDtoIsNull_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ Assert.ThrowsException(() => addressService.CreateAddress(null));
+ }
+
+ [TestMethod]
+ public void UpdateAddress_AddressUpdated_AddressReturned()
+ {
+ var mockContext = new Mock();
+
+ var address = new Address
+ {
+ Id = 1,
+ City = "City",
+ Country = "Country",
+ State = "State",
+ Street = "Street",
+ ZipCode = "ZipCode"
+ };
+
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([address]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ var addressDto = new AddressDto
+ {
+ City = "City2",
+ Country = "Country2",
+ State = "State2",
+ Street = "Street2",
+ ZipCode = "ZipCode2"
+ };
+
+ var result = addressService.UpdateAddress(1, addressDto);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("City2", result.City);
+ Assert.AreEqual("Country2", result.Country);
+ Assert.AreEqual("State2", result.State);
+ Assert.AreEqual("Street2", result.Street);
+ Assert.AreEqual("ZipCode2", result.ZipCode);
+ }
+
+ [TestMethod]
+ public void UpdateAddress_AddressDoesNotExist_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ var addressDto = new AddressDto
+ {
+ City = "City2",
+ Country = "Country2",
+ State = "State2",
+ Street = "Street2",
+ ZipCode = "ZipCode2"
+ };
+
+ Assert.ThrowsException(() => addressService.UpdateAddress(1, addressDto));
+ }
+
+ [TestMethod]
+ public void UpdateAddress_AddressDtoIsNull_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ Assert.ThrowsException(() => addressService.UpdateAddress(1, null));
+ }
+
+ [TestMethod]
+ public void DeleteAddress_AdddressExists_AddressDeleted()
+ {
+ var mockContext = new Mock();
+
+ var address = new Address
+ {
+ Id = 1,
+ City = "City",
+ Country = "Country",
+ State = "State",
+ Street = "Street",
+ ZipCode = "ZipCode"
+ };
+
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([address]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ addressService.DeleteAddress(1);
+
+ mockContext.Verify(c => c.SaveChanges(), Times.Once);
+ }
+
+ [TestMethod]
+ public void DeleteAddress_AddressDoesNotExist_NothingHappens()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([]);
+
+ var addressService = new AddressService(mockContext.Object);
+
+ Assert.ThrowsException(() => addressService.DeleteAddress(1));
+ mockContext.Verify(c => c.SaveChanges(), Times.Never);
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/CommentServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/CommentServiceTests.cs
new file mode 100644
index 00000000..26105028
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/CommentServiceTests.cs
@@ -0,0 +1,362 @@
+using Moq;
+using Moq.EntityFrameworkCore;
+using ReasnAPI.Exceptions;
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+using ReasnAPI.Models.Enums;
+using ReasnAPI.Services;
+
+namespace ReasnAPI.Tests.Services;
+
+[TestClass]
+public class CommentServiceTests
+{
+ [TestMethod]
+ public void GetCommentById_CommentExist_CommentReturned()
+ {
+ var mockContext = new Mock();
+
+ var event1 = new Event
+ {
+ Id = 1,
+ Name = "Event",
+ Description = "Description",
+ };
+
+ var user = new User
+ {
+ Id = 1,
+ Username = "Username",
+ Email = "Email",
+ Password = "Password",
+ };
+
+ var comment = new Comment
+ {
+ Id = 1,
+ Content = "Content",
+ EventId = event1.Id,
+ UserId = user.Id
+ };
+
+ var fakeComment = new FakeDbSet { comment };
+ var fakeEvent = new FakeDbSet { event1 };
+ var fakeUser = new FakeDbSet { user };
+
+ mockContext.Setup(c => c.Users).Returns(fakeUser);
+ mockContext.Setup(c => c.Events).Returns(fakeEvent);
+ mockContext.Setup(c => c.Comments).Returns(fakeComment);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ var result = commentService.GetCommentById(1);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("Content", result.Content);
+ Assert.AreEqual(1, result.EventId);
+ Assert.AreEqual(1, result.UserId);
+ }
+
+ [TestMethod]
+ public void GetCommentById_CommentDoesNotExist_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ Assert.ThrowsException(() => commentService.GetCommentById(1));
+ }
+
+ [TestMethod]
+ public void GetAllComments_CommentsExist_CommentsReturned()
+ {
+ var mockContext = new Mock();
+
+ var user = new User
+ {
+ Id = 1,
+ Username = "Username",
+ Email = "Email",
+ Password = "Password",
+ };
+
+ var event1 = new Event
+ {
+ Id = 1,
+ Name = "Event",
+ Description = "Description",
+ };
+
+ var comment1 = new Comment
+ {
+ Id = 1,
+ Content = "Content",
+ EventId = event1.Id,
+ UserId = user.Id
+ };
+
+ var comment2 = new Comment
+ {
+ Id = 2,
+ Content = "Content",
+ EventId = event1.Id,
+ UserId = user.Id
+ };
+
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+ mockContext.Setup(c => c.Events).ReturnsDbSet([event1]);
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([comment1, comment2]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ var result = commentService.GetAllComments();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(2, result.Count());
+ }
+
+ [TestMethod]
+ public void GetAllComments_NoComments_EmptyListReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ var result = commentService.GetAllComments();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(0, result.Count());
+ }
+
+ [TestMethod]
+ public void GetCommentsByFilter_CommentsExist_CommentsReturned()
+ {
+ var mockContext = new Mock();
+
+ var user = new User
+ {
+ Id = 1,
+ Username = "Username",
+ Email = "Email",
+ Password = "Password",
+ };
+
+ var event1 = new Event
+ {
+ Id = 1,
+ Name = "Event",
+ Description = "Description",
+ };
+
+ var comment1 = new Comment
+ {
+ Id = 1,
+ Content = "Content",
+ EventId = event1.Id,
+ UserId = user.Id
+ };
+
+ var comment2 = new Comment
+ {
+ Id = 2,
+ Content = "Content",
+ EventId = event1.Id,
+ UserId = user.Id
+ };
+
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+ mockContext.Setup(c => c.Events).ReturnsDbSet([event1]);
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([comment1, comment2]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ var result = commentService.GetCommentsByFilter(r => r.EventId == 1);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(2, result.Count());
+ }
+
+ [TestMethod]
+ public void GetCommentsByFilter_NoComments_EmptyListReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ var result = commentService.GetCommentsByFilter(r => r.EventId == 1);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(0, result.Count());
+ }
+
+ [TestMethod]
+ public void CreateComment_CommentCreated_CommentReturned()
+ {
+ var mockContext = new Mock();
+
+ var user = new User
+ {
+ Id = 1,
+ Username = "Username",
+ Email = "Email",
+ Password = "Password",
+ };
+
+ var event1 = new Event
+ {
+ Id = 1,
+ Name = "Event",
+ Description = "Description",
+ };
+
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+ mockContext.Setup(c => c.Events).ReturnsDbSet([event1]);
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ var commentDto = new CommentDto
+ {
+ Content = "Content",
+ UserId = 1,
+ EventId = 1
+ };
+
+ var result = commentService.CreateComment(commentDto);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("Content", result.Content);
+ Assert.AreEqual(1, result.EventId);
+ Assert.AreEqual(1, result.UserId);
+ }
+
+ [TestMethod]
+ public void CreateComment_CommentDtoIsNull_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ Assert.ThrowsException(() => commentService.CreateComment(null));
+ }
+
+ [TestMethod]
+ public void UpdateComment_CommentUpdated_CommentReturned()
+ {
+ var mockContext = new Mock();
+
+ var user = new User
+ {
+ Id = 1,
+ Username = "Username",
+ Email = "Email",
+ Password = "Password",
+ };
+
+ var event1 = new Event
+ {
+ Id = 1,
+ Name = "Event",
+ Description = "Description",
+ };
+
+ var comment = new Comment
+ {
+ Id = 1,
+ Content = "Content",
+ EventId = event1.Id,
+ UserId = user.Id
+ };
+
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+ mockContext.Setup(c => c.Events).ReturnsDbSet([event1]);
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([comment]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ var result = commentService.UpdateComment(1, new CommentDto
+ {
+ Content = "UpdatedContent",
+ EventId = 2,
+ UserId = 1
+ });
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("UpdatedContent", result.Content);
+ Assert.AreEqual(1, result.EventId);
+ Assert.AreEqual(1, result.UserId);
+ }
+
+ [TestMethod]
+ public void UpdateComment_CommentDoesNotExist_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ Assert.ThrowsException(() => commentService.UpdateComment(1, new CommentDto
+ {
+ Content = "UpdatedContent",
+ EventId = 2,
+ UserId = 1
+ }));
+ }
+
+ [TestMethod]
+ public void UpdateComment_CommentDtoIsNull_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ Assert.ThrowsException(() => commentService.UpdateComment(1, null));
+ }
+
+ [TestMethod]
+ public void DeleteComment_CommentExists_CommentDeleted()
+ {
+ var mockContext = new Mock();
+
+ var user = new User
+ {
+ Id = 1,
+ Username = "Username",
+ Email = "Email",
+ Password = "Password"
+ };
+
+ var comment = new Comment
+ {
+ Id = 1,
+ Content = "Content",
+ EventId = 1,
+ UserId = user.Id
+ };
+
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([comment]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ commentService.DeleteComment(1);
+
+ mockContext.Verify(c => c.SaveChanges(), Times.Once);
+ }
+
+ [TestMethod]
+ public void DeleteComment_CommentDoesNotExist_NothingHappens()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Comments).ReturnsDbSet([]);
+
+ var commentService = new CommentService(mockContext.Object);
+
+ Assert.ThrowsException(() => commentService.DeleteComment(1));
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/FakeDbSet.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/FakeDbSet.cs
new file mode 100644
index 00000000..d8c253a3
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/FakeDbSet.cs
@@ -0,0 +1,54 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.ChangeTracking;
+
+namespace ReasnAPI.Tests.Services
+{
+ public class FakeDbSet : DbSet, IAsyncEnumerable
+ where T : class
+ {
+ private readonly List _data;
+
+ public FakeDbSet()
+ {
+ _data = [];
+ }
+
+ public override T? Find(params object[] keyValues)
+ {
+ return _data.FirstOrDefault();
+ }
+
+ public override EntityEntry Add(T entity)
+ {
+ _data.Add(entity);
+ return null; // Return null for simplicity, adjust as needed
+ }
+
+ public override 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 readonly IEnumerator _enumerator = enumerator;
+
+ public ValueTask DisposeAsync()
+ {
+ _enumerator.Dispose();
+ return ValueTask.CompletedTask;
+ }
+
+ public ValueTask MoveNextAsync()
+ {
+ return new ValueTask(_enumerator.MoveNext());
+ }
+
+ public T Current => _enumerator.Current;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI.Tests/Services/ParticipantServiceTests.cs b/Server/ReasnAPI/ReasnAPI.Tests/Services/ParticipantServiceTests.cs
new file mode 100644
index 00000000..80057add
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/ParticipantServiceTests.cs
@@ -0,0 +1,365 @@
+using Moq;
+using Moq.EntityFrameworkCore;
+using ReasnAPI.Exceptions;
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+using ReasnAPI.Models.Enums;
+using ReasnAPI.Services;
+
+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()
+ {
+ var mockContext = new Mock();
+
+ var event1 = new Event
+ {
+ Id = 1,
+ Name = "Event",
+ Description = "Description"
+ };
+
+ var user1 = new User
+ {
+ Id = 1,
+ Username = "User",
+ Email = "Email",
+ Password = "Password"
+ };
+
+ var user2 = new User
+ {
+ Id = 2,
+ Username = "User",
+ Email = "Email",
+ Password = "Password"
+ };
+
+ var participant1 = new Participant
+ {
+ Id = 1,
+ EventId = event1.Id,
+ UserId = user1.Id,
+ Status = ParticipantStatus.Interested
+ };
+
+ var participant2 = new Participant
+ {
+ Id = 2,
+ EventId = event1.Id,
+ UserId = user2.Id,
+ Status = ParticipantStatus.Participating
+ };
+
+ mockContext.Setup(c => c.Events).ReturnsDbSet([event1]);
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user1, user2]);
+ mockContext.Setup(c => c.Participants).ReturnsDbSet([participant1, participant2]);
+
+ var participantService = new ParticipantService(mockContext.Object);
+
+ var result = participantService.GetAllParticipants();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(2, result.Count());
+ }
+
+ [TestMethod]
+ public void GetAllParticipants_NoParticipants_EmptyListReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Participants).ReturnsDbSet([]);
+
+ var participantService = new ParticipantService(mockContext.Object);
+
+ var result = participantService.GetAllParticipants();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(0, result.Count());
+ }
+
+ [TestMethod]
+ public void GetParticipantsByFilter_ParticipantsExist_ParticipantsReturned()
+ {
+ var mockContext = new Mock();
+
+ var event1 = new Event
+ {
+ Id = 1,
+ Name = "Event",
+ Description = "Description"
+ };
+
+ var user1 = new User
+ {
+ Id = 1,
+ Username = "User",
+ Email = "Email",
+ Password = "Password"
+ };
+
+ var user2 = new User
+ {
+ Id = 2,
+ Username = "User",
+ Email = "Email",
+ Password = "Password"
+ };
+
+ var participant1 = new Participant
+ {
+ Id = 1,
+ EventId = event1.Id,
+ UserId = user1.Id,
+ Status = ParticipantStatus.Interested
+ };
+
+ var participant2 = new Participant
+ {
+ Id = 2,
+ EventId = event1.Id,
+ UserId = user2.Id,
+ Status = ParticipantStatus.Participating
+ };
+
+ mockContext.Setup(c => c.Events).ReturnsDbSet([event1]);
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user1, user2]);
+ mockContext.Setup(c => c.Participants).ReturnsDbSet([participant1, participant2]);
+
+ var participantService = new ParticipantService(mockContext.Object);
+
+ var result = participantService.GetParticipantsByFilter(r => r.EventId == 1);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(2, result.Count());
+ }
+
+ [TestMethod]
+ public void GetParticipantsByFilter_ParticipantsDoNotExist_EmptyListReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Participants).ReturnsDbSet([]);
+
+ var participantService = new ParticipantService(mockContext.Object);
+
+ var result = participantService.GetParticipantsByFilter(r => r.EventId == 1);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(0, result.Count());
+ }
+
+ [TestMethod]
+ public void CreateParticipant_ParticipantCreated_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"
+ };
+
+ mockContext.Setup(c => c.Events).ReturnsDbSet([event1]);
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+ mockContext.Setup(c => c.Participants).ReturnsDbSet([]);
+
+ var participantService = new ParticipantService(mockContext.Object);
+
+ var participantDto = new ParticipantDto
+ {
+ EventId = 1,
+ UserId = 1,
+ Status = ParticipantStatus.Interested
+ };
+
+ var result = participantService.CreateParticipant(participantDto);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(1, result.EventId);
+ Assert.AreEqual(1, result.UserId);
+ Assert.AreEqual(ParticipantStatus.Interested, result.Status);
+ }
+
+ [TestMethod]
+ public void CreateParticipant_ParticipantDtoIsNull_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Participants).ReturnsDbSet([]);
+
+ var participantService = new ParticipantService(mockContext.Object);
+
+ Assert.ThrowsException(() => participantService.CreateParticipant(null));
+ }
+
+ [TestMethod]
+ public void UpdateParticipant_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
+ };
+
+ mockContext.Setup(c => c.Events).ReturnsDbSet([event1]);
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+ mockContext.Setup(c => c.Participants).ReturnsDbSet([participant]);
+
+ var participantService = new ParticipantService(mockContext.Object);
+
+ var result = participantService.UpdateParticipant(1, new ParticipantDto
+ {
+ EventId = 2,
+ UserId = 2,
+ Status = ParticipantStatus.Participating
+ });
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(1, result.EventId);
+ Assert.AreEqual(1, result.UserId);
+ Assert.AreEqual(ParticipantStatus.Participating, result.Status);
+ }
+
+ [TestMethod]
+ public void UpdateParticipant_ParticipantDoesNotExist_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Participants).ReturnsDbSet([]);
+
+ var participantService = new ParticipantService(mockContext.Object);
+
+ Assert.ThrowsException(() => participantService.UpdateParticipant(1, new ParticipantDto
+ {
+ EventId = 2,
+ UserId = 2,
+ Status = ParticipantStatus.Participating
+ }));
+ }
+
+ [TestMethod]
+ public void UpdateParticipant_ParticipantDtoIsNull_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Participants).ReturnsDbSet([
+ new() { Id = 1, EventId = 1, UserId = 1, Status = ParticipantStatus.Participating }
+ ]);
+
+ var participantService = new ParticipantService(mockContext.Object);
+
+ Assert.ThrowsException(() => participantService.UpdateParticipant(1, 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 participantService = new ParticipantService(mockContext.Object);
+
+ participantService.DeleteParticipant(1);
+
+ mockContext.Verify(c => c.SaveChanges(), Times.Once);
+ }
+
+ [TestMethod]
+ public void DeleteParticipant_ParticipantDoesNotExist_NothingDeleted()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Participants).ReturnsDbSet([]);
+
+ var participantService = new ParticipantService(mockContext.Object);
+
+ Assert.ThrowsException(() => participantService.DeleteParticipant(1));
+ 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
new file mode 100644
index 00000000..a7758b7e
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI.Tests/Services/UserServiceTests.cs
@@ -0,0 +1,214 @@
+using Moq;
+using Moq.EntityFrameworkCore;
+using ReasnAPI.Exceptions;
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+using ReasnAPI.Models.Enums;
+using ReasnAPI.Services;
+
+namespace ReasnAPI.Tests.Services;
+
+[TestClass]
+public class UserServiceTests
+{
+ [TestMethod]
+ public void GetUserById_UserDoesNotExist_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Users).ReturnsDbSet([]);
+
+ var userService = new UserService(mockContext.Object);
+
+ Assert.ThrowsException(() => userService.GetUserById(1));
+ }
+
+ [TestMethod]
+ public void GetUserById_UserExists_UserReturned()
+ {
+ var mockContext = new Mock();
+
+ var user = new User
+ {
+ Id = 1,
+ Name = "John",
+ Surname = "Doe",
+ Username = "Username",
+ Email = "Email"
+ };
+
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+
+ var userService = new UserService(mockContext.Object);
+
+ var result = userService.GetUserById(1);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("John", result.Name);
+ Assert.AreEqual("Doe", result.Surname);
+ Assert.AreEqual("Username", result.Username);
+ Assert.AreEqual("Email", result.Email);
+ }
+
+ [TestMethod]
+ public void GetUserByUsername_UserDoesNotExist_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Users).ReturnsDbSet([]);
+
+ var userService = new UserService(mockContext.Object);
+
+ Assert.ThrowsException(() => userService.GetUserByUsername("username"));
+ }
+
+ [TestMethod]
+ public void GetUserByUsername_UserExists_UserReturned()
+ {
+ var mockContext = new Mock();
+
+ var user = new User
+ {
+ Id = 1,
+ Name = "John",
+ Surname = "Doe",
+ Username = "Username",
+ Email = "Email"
+ };
+
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+
+ var userService = new UserService(mockContext.Object);
+
+ var result = userService.GetUserByUsername("Username");
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("John", result.Name);
+ Assert.AreEqual("Doe", result.Surname);
+ Assert.AreEqual("Username", result.Username);
+ Assert.AreEqual("Email", result.Email);
+ }
+
+ [TestMethod]
+ public void GetAllUsers_NoUsers_EmptyListReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Users).ReturnsDbSet([]);
+
+ var userService = new UserService(mockContext.Object);
+
+ var result = userService.GetAllUsers();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(0, result.Count());
+ }
+
+ [TestMethod]
+ public void GetUsersByFilter_NoUsers_EmptyListReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Users).ReturnsDbSet([]);
+
+ var userService = new UserService(mockContext.Object);
+
+ var result = userService.GetUsersByFilter(u => u.Name == "John");
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual(0, result.Count());
+ }
+
+ [TestMethod]
+ public void UpdateUser_UserUpdated_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",
+ AddressId = address.Id,
+ Role = UserRole.User
+ };
+
+ mockContext.Setup(c => c.Addresses).ReturnsDbSet([address]);
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+
+ var userService = new UserService(mockContext.Object);
+
+ var userDto = new UserDto
+ {
+ Name = "Jane",
+ Surname = "Doe",
+ Username = "username",
+ Email = "Email",
+ Phone = "Phone",
+ AddressId = address.Id,
+ Role = UserRole.User
+ };
+
+ var result = userService.UpdateUser(1, userDto);
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("Jane", result.Name);
+ Assert.AreEqual("Doe", result.Surname);
+ Assert.AreEqual("username", result.Username);
+ Assert.AreEqual("Email", result.Email);
+ Assert.AreEqual("Phone", result.Phone);
+ Assert.AreEqual(UserRole.User, result.Role);
+ Assert.AreEqual(1, result.AddressId);
+ }
+
+ [TestMethod]
+ public void UpdateUser_UserDtoIsNull_NullReturned()
+ {
+ var mockContext = new Mock();
+
+ var user = new User
+ {
+ Id = 1,
+ Name = "John",
+ Surname = "Doe",
+ Username = "Username",
+ Email = "Email"
+ };
+
+ mockContext.Setup(c => c.Users).ReturnsDbSet([user]);
+
+ var userService = new UserService(mockContext.Object);
+
+ Assert.ThrowsException(() => userService.UpdateUser(1, null));
+ }
+
+ [TestMethod]
+ public void UpdateUser_UserDoesNotExist_NullReturned()
+ {
+ var mockContext = new Mock();
+ mockContext.Setup(c => c.Users).ReturnsDbSet([]);
+
+ var userService = new UserService(mockContext.Object);
+
+ var userDto = new UserDto
+ {
+ Name = "Jane",
+ Surname = "Doe",
+ Username = "Username",
+ Email = "Email",
+ Phone = "Phone",
+ Role = UserRole.User,
+ AddressId = 1
+ };
+
+ Assert.ThrowsException(() => userService.UpdateUser(1, userDto));
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI.Tests/UnitTest1.cs b/Server/ReasnAPI/ReasnAPI.Tests/UnitTest1.cs
deleted file mode 100644
index 46dac6f2..00000000
--- a/Server/ReasnAPI/ReasnAPI.Tests/UnitTest1.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace ReasnAPI.Tests
-{
- [TestClass]
- public class UnitTest1
- {
- [TestMethod]
- public void TestAdd()
- {
- Assert.AreEqual(17, 17);
- }
- }
-}
diff --git a/Server/ReasnAPI/ReasnAPI/Exceptions/BadRequestException.cs b/Server/ReasnAPI/ReasnAPI/Exceptions/BadRequestException.cs
new file mode 100644
index 00000000..b0198d2c
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI/Exceptions/BadRequestException.cs
@@ -0,0 +1,10 @@
+namespace ReasnAPI.Exceptions;
+
+public class BadRequestException : Exception
+{
+ public BadRequestException() { }
+
+ public BadRequestException(string message) : base(message) { }
+
+ public BadRequestException(string message, Exception inner) : base(message, inner) { }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Exceptions/NotFoundException.cs b/Server/ReasnAPI/ReasnAPI/Exceptions/NotFoundException.cs
new file mode 100644
index 00000000..1ca7f981
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI/Exceptions/NotFoundException.cs
@@ -0,0 +1,10 @@
+namespace ReasnAPI.Exceptions;
+
+public class NotFoundException : Exception
+{
+ public NotFoundException() { }
+
+ public NotFoundException(string message) : base(message) { }
+
+ public NotFoundException(string message, Exception innerException) : base(message, innerException) { }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/AddressMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/AddressMapper.cs
new file mode 100644
index 00000000..8fe399dc
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI/Mappers/AddressMapper.cs
@@ -0,0 +1,36 @@
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+
+namespace ReasnAPI.Mappers;
+
+public static class AddressMapper
+{
+ public static AddressDto ToDto(this Address address)
+ {
+ return new AddressDto
+ {
+ Street = address.Street,
+ Country = address.Country,
+ City = address.City,
+ State = address.State,
+ ZipCode = address.ZipCode
+ };
+ }
+
+ public static List ToDtoList(this IEnumerable addresses)
+ {
+ return addresses.Select(ToDto).ToList();
+ }
+
+ public static Address ToEntity(this AddressDto addressDto)
+ {
+ return new Address
+ {
+ City = addressDto.City,
+ Country = addressDto.Country,
+ State = addressDto.State,
+ Street = addressDto.Street,
+ ZipCode = addressDto.ZipCode
+ };
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/CommentMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/CommentMapper.cs
new file mode 100644
index 00000000..fc785ef4
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI/Mappers/CommentMapper.cs
@@ -0,0 +1,34 @@
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+
+namespace ReasnAPI.Mappers;
+
+public static class CommentMapper
+{
+ public static CommentDto ToDto(this Comment comment)
+ {
+ return new CommentDto
+ {
+ EventId = comment.EventId,
+ Content = comment.Content,
+ UserId = comment.UserId,
+ CreatedAt = comment.CreatedAt
+ };
+ }
+
+ public static List ToDtoList(this IEnumerable comments)
+ {
+ return comments.Select(ToDto).ToList();
+ }
+
+ public static Comment ToEntity(this CommentDto commentDto)
+ {
+ return new Comment
+ {
+ EventId = commentDto.EventId,
+ Content = commentDto.Content,
+ UserId = commentDto.UserId,
+ CreatedAt = commentDto.CreatedAt
+ };
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/InterestMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/InterestMapper.cs
new file mode 100644
index 00000000..f6f75f72
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI/Mappers/InterestMapper.cs
@@ -0,0 +1,28 @@
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+
+namespace ReasnAPI.Mappers;
+
+public static class InterestMapper
+{
+ public static InterestDto ToDto(this Interest interest)
+ {
+ return new InterestDto
+ {
+ Name = interest.Name
+ };
+ }
+
+ public static List ToDtoList(this IEnumerable interests)
+ {
+ return interests.Select(ToDto).ToList();
+ }
+
+ public static Interest ToEntity(this InterestDto interestDto)
+ {
+ return new Interest
+ {
+ Name = interestDto.Name
+ };
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/ParticipantMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/ParticipantMapper.cs
new file mode 100644
index 00000000..66fee4e6
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI/Mappers/ParticipantMapper.cs
@@ -0,0 +1,31 @@
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+
+namespace ReasnAPI.Mappers;
+public static class ParticipantMapper
+{
+ public static ParticipantDto ToDto(this Participant participant)
+ {
+ return new ParticipantDto
+ {
+ EventId = participant.EventId,
+ UserId = participant.UserId,
+ Status = participant.Status
+ };
+ }
+
+ public static List ToDtoList(this IEnumerable participants)
+ {
+ 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
new file mode 100644
index 00000000..9d1ba415
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI/Mappers/UserInterestMapper.cs
@@ -0,0 +1,30 @@
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+
+namespace ReasnAPI.Mappers;
+
+public static class UserInterestMapper
+{
+ public static UserInterestDto ToDto(this UserInterest userInterest)
+ {
+ return new UserInterestDto
+ {
+ Interest = userInterest.Interest.ToDto(),
+ Level = userInterest.Level
+ };
+ }
+
+ public static List ToDtoList(this IEnumerable userInterests)
+ {
+ return userInterests.Select(ToDto).ToList();
+ }
+
+ public static UserInterest ToEntity(this UserInterestDto userInterestDto)
+ {
+ return new UserInterest
+ {
+ Interest = userInterestDto.Interest.ToEntity(),
+ Level = userInterestDto.Level
+ };
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Mappers/UserMapper.cs b/Server/ReasnAPI/ReasnAPI/Mappers/UserMapper.cs
new file mode 100644
index 00000000..836f77b7
--- /dev/null
+++ b/Server/ReasnAPI/ReasnAPI/Mappers/UserMapper.cs
@@ -0,0 +1,27 @@
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+
+namespace ReasnAPI.Mappers;
+
+public static class UserMapper
+{
+ public static UserDto ToDto(this User user)
+ {
+ return new UserDto
+ {
+ Username = user.Username,
+ Name = user.Name,
+ Surname = user.Surname,
+ Email = user.Email,
+ Phone = user.Phone,
+ Role = user.Role,
+ AddressId = user.AddressId,
+ Interests = user.UserInterests.ToDtoList()
+ };
+ }
+
+ public static List ToDtoList(this IEnumerable users)
+ {
+ return users.Select(ToDto).ToList();
+ }
+}
\ 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 f06d2717..cc71de38 100644
--- a/Server/ReasnAPI/ReasnAPI/Models/DTOs/UserDto.cs
+++ b/Server/ReasnAPI/ReasnAPI/Models/DTOs/UserDto.cs
@@ -1,7 +1,9 @@
using ReasnAPI.Models.Enums;
-namespace ReasnAPI.Models.DTOs {
- public class UserDto {
+namespace ReasnAPI.Models.DTOs
+{
+ public class UserDto
+ {
public string Username { get; set; } = null!;
public string Name { get; set; } = null!;
public string Surname { get; set; } = null!;
diff --git a/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs b/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs
index 041cf80b..9a2c04d1 100644
--- a/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs
+++ b/Server/ReasnAPI/ReasnAPI/Models/Database/ReasnContext.cs
@@ -37,7 +37,12 @@ public ReasnContext(DbContextOptions options)
public virtual DbSet UserInterests { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
- => optionsBuilder.UseNpgsql("name=ConnectionStrings:DefaultValue");
+ {
+ if (!optionsBuilder.IsConfigured)
+ {
+ optionsBuilder.UseNpgsql("name=ConnectionStrings:DefaultValue");
+ }
+ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -47,26 +52,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.HasPostgresEnum("common", "participant_status", new[] { "Interested", "Participating" })
.HasPostgresEnum("users", "role", new[] { "User", "Organizer", "Admin" });
- modelBuilder
- .Entity()
- .Property(u => u.Role)
- .HasConversion();
-
- modelBuilder
- .Entity()
- .Property(u => u.Status)
- .HasConversion();
-
- modelBuilder
- .Entity()
- .Property(u => u.ObjectType)
- .HasConversion ();
-
- modelBuilder
- .Entity()
- .Property(u => u.Status)
- .HasConversion();
-
modelBuilder.Entity(entity =>
{
entity.HasKey(e => e.Id).HasName("address_pkey");
diff --git a/Server/ReasnAPI/ReasnAPI/Models/Enums/EventStatus.cs b/Server/ReasnAPI/ReasnAPI/Models/Enums/EventStatus.cs
index beeb82e1..40c96dba 100644
--- a/Server/ReasnAPI/ReasnAPI/Models/Enums/EventStatus.cs
+++ b/Server/ReasnAPI/ReasnAPI/Models/Enums/EventStatus.cs
@@ -1,9 +1,15 @@
-namespace ReasnAPI.Models.Enums;
+using NpgsqlTypes;
+
+namespace ReasnAPI.Models.Enums;
public enum EventStatus
{
- Completed,
- Inprogress,
- Approved,
+ [PgName("Completed")]
+ Completed,
+ [PgName("InProgress")]
+ Inprogress,
+ [PgName("Approved")]
+ Approved,
+ [PgName("WaitingForApproval")]
WaitingForApproval
}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Models/Enums/ObjectType.cs b/Server/ReasnAPI/ReasnAPI/Models/Enums/ObjectType.cs
index bb194063..b2ea9d6b 100644
--- a/Server/ReasnAPI/ReasnAPI/Models/Enums/ObjectType.cs
+++ b/Server/ReasnAPI/ReasnAPI/Models/Enums/ObjectType.cs
@@ -1,7 +1,11 @@
-namespace ReasnAPI.Models.Enums;
+using NpgsqlTypes;
+
+namespace ReasnAPI.Models.Enums;
public enum ObjectType
{
+ [PgName("Event")]
Event,
+ [PgName("User")]
User
}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Models/Enums/ParticipantStatus.cs b/Server/ReasnAPI/ReasnAPI/Models/Enums/ParticipantStatus.cs
index 0584f578..057bbdb4 100644
--- a/Server/ReasnAPI/ReasnAPI/Models/Enums/ParticipantStatus.cs
+++ b/Server/ReasnAPI/ReasnAPI/Models/Enums/ParticipantStatus.cs
@@ -1,7 +1,11 @@
-namespace ReasnAPI.Models.Enums;
+using NpgsqlTypes;
+
+namespace ReasnAPI.Models.Enums;
public enum ParticipantStatus
{
+ [PgName("Interested")]
Interested,
+ [PgName("Participating")]
Participating
}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Models/Enums/UserRole.cs b/Server/ReasnAPI/ReasnAPI/Models/Enums/UserRole.cs
index 4a35be42..c9ced2b9 100644
--- a/Server/ReasnAPI/ReasnAPI/Models/Enums/UserRole.cs
+++ b/Server/ReasnAPI/ReasnAPI/Models/Enums/UserRole.cs
@@ -1,8 +1,13 @@
-namespace ReasnAPI.Models.Enums;
+using NpgsqlTypes;
+
+namespace ReasnAPI.Models.Enums;
public enum UserRole
{
+ [PgName("User")]
User,
+ [PgName("Organizer")]
Organizer,
+ [PgName("Admin")]
Admin
}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Program.cs b/Server/ReasnAPI/ReasnAPI/Program.cs
index 67d181aa..8b2051c9 100644
--- a/Server/ReasnAPI/ReasnAPI/Program.cs
+++ b/Server/ReasnAPI/ReasnAPI/Program.cs
@@ -7,21 +7,25 @@
using System;
using System.Text.Json.Serialization;
using ReasnAPI.Models.Database;
+using ReasnAPI.Services;
var builder = WebApplication.CreateSlimBuilder(args);
-builder.Services.AddControllers();
-var dataSourceBuilder = new NpgsqlDataSourceBuilder(builder.Configuration.GetConnectionString("DefaultValue"));
-dataSourceBuilder.MapEnum("events.participant.status");
-dataSourceBuilder.MapEnum("events.event.status");
-dataSourceBuilder.MapEnum("common.image.object_type");
-dataSourceBuilder.MapEnum("users.user.role");
-
-var dataSource = dataSourceBuilder.Build();
-// todo: uncomment after creating DbContext and change context name and if needed - connection string localized in appsettings.json
-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.AddControllers();
+
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
diff --git a/Server/ReasnAPI/ReasnAPI/Services/AddressService.cs b/Server/ReasnAPI/ReasnAPI/Services/AddressService.cs
index 04f944f5..86e6eb8c 100644
--- a/Server/ReasnAPI/ReasnAPI/Services/AddressService.cs
+++ b/Server/ReasnAPI/ReasnAPI/Services/AddressService.cs
@@ -1,16 +1,83 @@
-using ReasnAPI.Models.Database;
-
-namespace ReasnAPI.Services {
- public class AddressService (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 ReasnAPI.Exceptions;
+using ReasnAPI.Mappers;
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+using System.Linq.Expressions;
+
+namespace ReasnAPI.Services;
+
+public class AddressService(ReasnContext context)
+{
+ public AddressDto CreateAddress(AddressDto addressDto)
+ {
+ ArgumentNullException.ThrowIfNull(addressDto);
+
+ context.Addresses.Add(addressDto.ToEntity());
+ context.SaveChanges();
+
+ return addressDto;
+ }
+
+ public AddressDto UpdateAddress(int addressId, AddressDto addressDto)
+ {
+ ArgumentNullException.ThrowIfNull(addressDto);
+
+ var address = context.Addresses.FirstOrDefault(r => r.Id == addressId);
+
+ if (address is null)
+ {
+ throw new NotFoundException("Address not found");
+ }
+
+ address.City = addressDto.City;
+ address.Country = addressDto.Country;
+ address.Street = addressDto.Street;
+ address.State = addressDto.State;
+ address.ZipCode = addressDto.ZipCode;
+
+ context.Addresses.Update(address);
+ context.SaveChanges();
+
+ return address.ToDto();
+ }
+
+ public void DeleteAddress(int addressId)
+ {
+ var address = context.Addresses.FirstOrDefault(r => r.Id == addressId);
+
+ if (address is null)
+ {
+ throw new NotFoundException("Address not found");
+ }
+
+ context.Addresses.Remove(address);
+ context.SaveChanges();
+ }
+
+ public AddressDto GetAddressById(int addressId)
+ {
+ var address = context.Addresses.Find(addressId);
+
+ if (address is null)
+ {
+ throw new NotFoundException("Address not found");
+ }
+
+ return address.ToDto();
+ }
+
+ public IEnumerable GetAddressesByFilter(Expression> filter)
+ {
+ return context.Addresses
+ .Where(filter)
+ .ToDtoList()
+ .AsEnumerable();
+ }
+
+ public IEnumerable GetAllAddresses()
+ {
+ return context.Addresses
+ .ToDtoList()
+ .AsEnumerable();
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Services/CommentService.cs b/Server/ReasnAPI/ReasnAPI/Services/CommentService.cs
index 7b0767c0..95eac468 100644
--- a/Server/ReasnAPI/ReasnAPI/Services/CommentService.cs
+++ b/Server/ReasnAPI/ReasnAPI/Services/CommentService.cs
@@ -1,16 +1,88 @@
-using ReasnAPI.Models.Database;
-
-namespace ReasnAPI.Services {
- public class CommentService (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 ReasnAPI.Exceptions;
+using ReasnAPI.Mappers;
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+using System.Linq.Expressions;
+
+namespace ReasnAPI.Services;
+
+public class CommentService(ReasnContext context)
+{
+ public CommentDto CreateComment(CommentDto commentDto)
+ {
+ ArgumentNullException.ThrowIfNull(commentDto);
+
+ commentDto.CreatedAt = DateTime.UtcNow;
+
+ context.Comments.Add(commentDto.ToEntity());
+ context.SaveChanges();
+
+ return commentDto;
+ }
+
+ public CommentDto UpdateComment(int commentId, CommentDto commentDto)
+ {
+ ArgumentNullException.ThrowIfNull(commentDto);
+
+ var comment = context.Comments.FirstOrDefault(r => r.Id == commentId);
+
+ if (comment is null)
+ {
+ throw new NotFoundException("Comment not found");
+ }
+
+ var user = context.Users.FirstOrDefault(r => r.Id == commentDto.UserId);
+
+ if (user is null)
+ {
+ throw new NotFoundException("User not found");
+ }
+
+ comment.Content = commentDto.Content;
+
+ context.Comments.Update(comment);
+ context.SaveChanges();
+
+ return comment.ToDto();
+ }
+
+ public void DeleteComment(int commentId)
+ {
+ var comment = context.Comments.FirstOrDefault(r => r.Id == commentId);
+
+ if (comment is null)
+ {
+ throw new NotFoundException("Comment not found");
+ }
+
+ context.Comments.Remove(comment);
+ context.SaveChanges();
+ }
+
+ public CommentDto GetCommentById(int commentId)
+ {
+ var comment = context.Comments.Find(commentId);
+
+ if (comment is null)
+ {
+ throw new NotFoundException("Comment not found");
+ }
+
+ return comment.ToDto();
+ }
+
+ public IEnumerable GetCommentsByFilter(Expression> filter)
+ {
+ return context.Comments
+ .Where(filter)
+ .ToDtoList()
+ .AsEnumerable();
+ }
+
+ public IEnumerable GetAllComments()
+ {
+ return context.Comments
+ .ToDtoList()
+ .AsEnumerable();
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Services/ObjectTypeService.cs b/Server/ReasnAPI/ReasnAPI/Services/ObjectTypeService.cs
deleted file mode 100644
index 84ba4abc..00000000
--- a/Server/ReasnAPI/ReasnAPI/Services/ObjectTypeService.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using ReasnAPI.Models.Database;
-
-namespace ReasnAPI.Services {
- public class ObjectTypeService (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/ParticipantService.cs b/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs
index c3f4cd68..2b568ddb 100644
--- a/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs
+++ b/Server/ReasnAPI/ReasnAPI/Services/ParticipantService.cs
@@ -1,16 +1,99 @@
-using ReasnAPI.Models.Database;
-
-namespace ReasnAPI.Services {
- public class ParticipantService (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 ReasnAPI.Exceptions;
+using ReasnAPI.Mappers;
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+using System.Linq.Expressions;
+
+namespace ReasnAPI.Services;
+
+public class ParticipantService(ReasnContext context)
+{
+ public ParticipantDto CreateParticipant(ParticipantDto participantDto)
+ {
+ ArgumentNullException.ThrowIfNull(participantDto);
+
+ var participantDb = context.Participants.FirstOrDefault(r => r.Event.Id == participantDto.EventId && r.User.Id == participantDto.UserId);
+
+ if (participantDb is not null)
+ {
+ throw new BadRequestException("Participant already exists");
+ }
+
+ var userInDb = context.Users.FirstOrDefault(r => r.Id == participantDto.UserId);
+
+ if (userInDb is null)
+ {
+ throw new NotFoundException("User not found");
+ }
+
+ var eventInDb = context.Events.FirstOrDefault(r => r.Id == participantDto.EventId);
+
+ if (eventInDb is null)
+ {
+ throw new NotFoundException("Event not found");
+ }
+
+ 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)
+ {
+ throw new NotFoundException("Participant not found");
+ }
+
+ participant.Status = participantDto.Status;
+
+ context.Participants.Update(participant);
+ context.SaveChanges();
+
+ return participant.ToDto();
+ }
+
+ public void DeleteParticipant(int participantId)
+ {
+ var participant = context.Participants.FirstOrDefault(r => r.Id == participantId);
+
+ if (participant is null)
+ {
+ throw new NotFoundException("Participant not found");
+ }
+
+ context.Participants.Remove(participant);
+ context.SaveChanges();
+ }
+
+ public ParticipantDto GetParticipantById(int participantId)
+ {
+ var participant = context.Participants.Find(participantId);
+
+ if (participant is null)
+ {
+ throw new NotFoundException("Participant not found");
+ }
+
+ return participant.ToDto();
+ }
+
+ public IEnumerable GetParticipantsByFilter(Expression> filter)
+ {
+ return context.Participants
+ .Where(filter)
+ .ToDtoList()
+ .AsEnumerable();
+ }
+
+ public IEnumerable GetAllParticipants()
+ {
+ return context.Participants
+ .ToDtoList()
+ .AsEnumerable();
+ }
+}
\ No newline at end of file
diff --git a/Server/ReasnAPI/ReasnAPI/Services/RoleService.cs b/Server/ReasnAPI/ReasnAPI/Services/RoleService.cs
deleted file mode 100644
index 6dea686b..00000000
--- a/Server/ReasnAPI/ReasnAPI/Services/RoleService.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using ReasnAPI.Models.Database;
-
-namespace ReasnAPI.Services {
- public class RoleService (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/StatusService.cs b/Server/ReasnAPI/ReasnAPI/Services/StatusService.cs
deleted file mode 100644
index 2af9b1ad..00000000
--- a/Server/ReasnAPI/ReasnAPI/Services/StatusService.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using ReasnAPI.Models.Database;
-
-namespace ReasnAPI.Services {
- public class StatusService (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/UserService.cs b/Server/ReasnAPI/ReasnAPI/Services/UserService.cs
index f657fcd8..92a187a7 100644
--- a/Server/ReasnAPI/ReasnAPI/Services/UserService.cs
+++ b/Server/ReasnAPI/ReasnAPI/Services/UserService.cs
@@ -1,16 +1,152 @@
-using ReasnAPI.Models.Database;
-
-namespace ReasnAPI.Services {
- public class UserService (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.Mappers;
+using ReasnAPI.Models.Database;
+using ReasnAPI.Models.DTOs;
+using System.Linq.Expressions;
+using System.Transactions;
+
+namespace ReasnAPI.Services;
+
+public class UserService(ReasnContext context)
+{
+ public UserDto UpdateUser(int userId, UserDto userDto)
+ {
+ using (var scope = new TransactionScope())
+ {
+ ArgumentNullException.ThrowIfNull(userDto);
+
+ var user = context.Users
+ .Include(u => u.UserInterests)
+ .ThenInclude(ui => ui.Interest)
+ .FirstOrDefault(r => r.Id == userId);
+
+ if (user is null)
+ {
+ throw new NotFoundException("User not found");
+ }
+
+ var usernameExists = context.Users.Any(r => r.Username == userDto.Username && r.Id != userId);
+
+ if (usernameExists)
+ {
+ throw new BadRequestException("User with given username already exists");
+ }
+
+ var emailExists = context.Users.Any(r => r.Email == userDto.Email && r.Id != userId);
+
+ if (emailExists)
+ {
+ throw new BadRequestException("User with given email already exists");
+ }
+
+ var phoneExists = context.Users.Any(r => r.Phone == userDto.Phone && r.Id != userId);
+
+ 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);
+
+ if (userDto.Interests is null || userDto.Interests.Count == 0)
+ {
+ context.SaveChanges();
+ scope.Complete();
+ 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);
+
+ var interestsToUpdate = user.UserInterests
+ .Where(ui => userDto.Interests.Exists(uid => uid.Interest.Name == ui.Interest.Name))
+ .ToList();
+
+ foreach (var interest in interestsToUpdate)
+ {
+ var updatedInterest = userDto.Interests.Find(uid => uid.Interest.Name == interest.Interest.Name);
+
+ if (updatedInterest is null)
+ {
+ continue;
+ }
+
+ interest.Level = updatedInterest.Level;
+ context.UserInterests.Update(interest);
+ }
+
+ context.SaveChanges();
+ scope.Complete();
+ }
+
+ return userDto;
+ }
+
+ public UserDto GetUserById(int userId)
+ {
+ var user = context.Users
+ .Include(u => u.UserInterests)
+ .FirstOrDefault(u => u.Id == userId);
+
+ if (user is null)
+ {
+ throw new NotFoundException("User not found");
+ }
+
+ return user.ToDto();
+ }
+
+ public UserDto GetUserByUsername(string username)
+ {
+ var user = context.Users
+ .Include(u => u.UserInterests)
+ .FirstOrDefault(u => u.Username == username);
+
+ if (user is null)
+ {
+ throw new NotFoundException("User not found");
+ }
+
+ return user.ToDto();
+ }
+
+ public IEnumerable GetUsersByFilter(Expression> filter)
+ {
+ return context.Users
+ .Include(u => u.UserInterests)
+ .ThenInclude(ui => ui.Interest)
+ .Where(filter)
+ .ToDtoList()
+ .AsEnumerable();
+ }
+
+ public IEnumerable GetAllUsers()
+ {
+ var users = context.Users
+ .Include(u => u.UserInterests)
+ .ThenInclude(ui => ui.Interest)
+ .ToList();
+
+ return users.ToDtoList().AsEnumerable();
+ }
+}
\ No newline at end of file