diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/BookingsController.cs b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/BookingsController.cs new file mode 100644 index 000000000..1b7019435 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/BookingsController.cs @@ -0,0 +1,42 @@ +using CineFlex.Application.Features.Bookings.CQRS.Handlers; +using CineFlex.Application.Features.Bookings.CQRS.Requests.Commands; +using CineFlex.Application.Features.Bookings.CQRS.Requests.Queries; +using CineFlex.Application.Features.Bookings.DTOs; +using CineFlex.Application.Features.Movies.CQRS.Commands; +using CineFlex.Application.Features.Movies.CQRS.Queries; +using CineFlex.Application.Features.Movies.DTOs; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace CineFlex.API.Controllers; + +public class BookingsController : BaseApiController +{ + private readonly IMediator _mediator; + + public BookingsController(IMediator mediator) + { + _mediator = mediator; + } + + [HttpGet("{id}")] + public async Task Get(int id) + { + return HandleResult(await _mediator.Send(new GetBookingDetailQuery() { Id = id })); + + } + + [HttpPost] + public async Task Post([FromBody] CreateBookingDto createBookingDto) + { + var command = new CreateBookingCommand() {CreateBookingDto = createBookingDto }; + return HandleResult(await _mediator.Send(command)); + } + + [HttpDelete("{id}")] + public async Task Delete(int id) + { + var command = new DeleteBookingCommand() { Id = id }; + return HandleResult(await _mediator.Send(command)); + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/MovieController.cs b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/MovieController.cs index 4a843cd09..81aaa8c78 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/MovieController.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/MovieController.cs @@ -34,6 +34,7 @@ public async Task Get(int id) [HttpPost] public async Task Post([FromBody] CreateMovieDto createMovie) { + // TODO: var command = new CreateMovieCommand { MovieDto = createMovie }; return HandleResult(await _mediator.Send(command)); } @@ -41,8 +42,6 @@ public async Task Post([FromBody] CreateMovieDto createMovie) [HttpPut] public async Task Put([FromBody] UpdateMovieDto movieDto) { - - var command = new UpdateMovieCommand { MovieDto = movieDto }; return HandleResult(await _mediator.Send(command)); } diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/SeatsController.cs b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/SeatsController.cs new file mode 100644 index 000000000..7b31c3912 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/SeatsController.cs @@ -0,0 +1,55 @@ +using CineFlex.Application.Features.Movies.CQRS.Commands; +using CineFlex.Application.Features.Movies.CQRS.Queries; +using CineFlex.Application.Features.Movies.DTOs; +using CineFlex.Application.Features.Seats.CQRS.Requests.Commands; +using CineFlex.Application.Features.Seats.CQRS.Requests.Queries; +using CineFlex.Application.Features.Seats.DTOs; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace CineFlex.API.Controllers; + +public class SeatsController: BaseApiController +{ + + private readonly IMediator _mediator; + + public SeatsController(IMediator mediator) + { + _mediator = mediator; + } + + [HttpPost("all")] + public async Task>> Get([FromBody] GetAllSeatsDto getAllSeatsDto) + { + return HandleResult(await _mediator.Send(new GetAllSeatsQuery(){GetAllSeatsDto = getAllSeatsDto})); + } + + [HttpGet("{id}")] + public async Task Get(int id) + { + return HandleResult(await _mediator.Send(new GetSeatDetailsQuery { Id = id })); + + } + + [HttpPost] + public async Task Post([FromBody] CreateSeatDto createSeatDto) + { + var command = new CreateSeatCommand() { CreateSeatDto = createSeatDto }; + return HandleResult(await _mediator.Send(command)); + } + + [HttpPut] + public async Task Put([FromBody] UpdateSeatDto updateSeatDto) + { + var command = new UpdateSeatCommand { UpdateSeatDto = updateSeatDto}; + return HandleResult(await _mediator.Send(command)); + } + + [HttpDelete("{id}")] + public async Task Delete(int id) + { + var command = new DeleteSeatCommand { Id = id }; + return HandleResult(await _mediator.Send(command)); + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/UsersController.cs b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/UsersController.cs new file mode 100644 index 000000000..c4dace11f --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/UsersController.cs @@ -0,0 +1,68 @@ +using System.Security.Claims; +using CineFlex.Domain; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; + +namespace CineFlex.API.Controllers; + +public class UsersController: BaseApiController +{ + + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public UsersController(UserManager userManager, SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + + [HttpPost("/login")] + public async Task Login([FromBody] LoginInfo loginInfo) + { + var user = await _userManager.FindByEmailAsync(email: loginInfo.email); + + if (user == null) + return NotFound("User not found"); + + var signInResult = await _signInManager.PasswordSignInAsync(user, loginInfo.password, false, false); + + if (signInResult.Succeeded) + return Ok("SUCCEEDED"); + + return Ok("DENIED"); + } + + [HttpPost("/signup")] + public async Task Signup([FromBody] SignupInfo signupInfo) + { + var newUser = new User { UserName = signupInfo.username, Email = signupInfo.email}; + + var result = await _userManager.CreateAsync(newUser, signupInfo.password); + + if (result.Succeeded) + { + var claimResult = await _userManager.AddClaimAsync(newUser, new Claim("Role", "User")); + + if (claimResult.Succeeded) + return Ok("Created"); + } + + return Ok(result.Errors.First().Description); + } + + public class LoginInfo + { + public string email { get; set; } + public string password { get; set; } + } + + public class SignupInfo + { + public string username { get; set; } + public string email { get; set; } + public string password { get; set; } + } + +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/Program.cs b/Backend/backend_assessment/CineFlex/CineFlex.API/Program.cs index 4e1afd370..2a4e6000c 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.API/Program.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/Program.cs @@ -1,4 +1,5 @@ using CineFlex.Application; +using CineFlex.Domain; using CineFlex.Persistence; using Microsoft.OpenApi.Models; using Microsoft.AspNetCore.Identity; @@ -12,6 +13,12 @@ AddSwaggerDoc(builder.Services); builder.Services.AddControllers(); +// builder.Services.AddIdentity(options => +// { +// options.User.RequireUniqueEmail = true; +// options.SignIn.RequireConfirmedAccount = false; +// }) +// .AddEntityFrameworkStores(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/Properties/launchSettings.json b/Backend/backend_assessment/CineFlex/CineFlex.API/Properties/launchSettings.json index 76f557e56..efff2db5a 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.API/Properties/launchSettings.json +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/Properties/launchSettings.json @@ -21,7 +21,7 @@ }, "IIS Express": { "commandName": "IISExpress", - "launchBrowser": true, + "launchBrowser": false, "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/appsettings.json b/Backend/backend_assessment/CineFlex/CineFlex.API/appsettings.json index 2b720c223..a9a2a3a9f 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.API/appsettings.json +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "CineFlexConnectionString": "User ID=postgres;Password=1234;Server=localhost;Port=5432;Database=CineFlex;Integrated Security=true;Pooling=true;" + "CineFlexConnectionString": "User ID=postgres;Password=dr;Server=localhost;Database=CineFlex" }, "Logging": { "LogLevel": { diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/CineFlex.Application.UnitTest.csproj b/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/CineFlex.Application.UnitTest.csproj index 391cc02f8..72a75b4ba 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/CineFlex.Application.UnitTest.csproj +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/CineFlex.Application.UnitTest.csproj @@ -28,8 +28,4 @@ - - - - diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Mocks/MockSeatRepository.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Mocks/MockSeatRepository.cs new file mode 100644 index 000000000..565e29222 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Mocks/MockSeatRepository.cs @@ -0,0 +1,58 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Domain; +using Moq; + +namespace BlogApp.Tests.Mocks; + +public static class MockSeatRepository +{ + public static Mock GetBlogRepository() + { + var seats = new List + { + new () + { + Movie = new Movie(){Genre = "genre", Title = "Title"}, + Cinema = new CinemaEntity() {Location = "the location", Name = "The name"}, + Location = "kjdkjsd", + Taken = false, + } + }; + + var mockRepo = new Mock(); + + mockRepo.Setup(r => r.GetAll()).ReturnsAsync(seats); + + mockRepo.Setup(r => r.Add(It.IsAny())).ReturnsAsync((Seat seat) => + { + seat.Id = seats.Count() + 1; + seats.Add(seat); + return seat; + }); + + mockRepo.Setup(r => r.Update(It.IsAny())).Callback((Seat seat) => + { + var newSeats = seats.Where((r) => r.Id != seat.Id).ToList(); + newSeats.Add(seat); + }); + + mockRepo.Setup(r => r.Delete(It.IsAny())).Callback((Seat seat) => + { + if (seats.Exists(b => b.Id == seat.Id)) + seats.Remove(seats.Find(b => b.Id == seat.Id)!); + }); + + mockRepo.Setup(r => r.Exists(It.IsAny())).ReturnsAsync((int id) => + { + var rate = seats.FirstOrDefault((r) => r.Id == id); + return rate != null; + }); + + mockRepo.Setup(r => r.Get(It.IsAny()))!.ReturnsAsync((int id) => + { + return seats.FirstOrDefault((r) => r.Id == id); + }); + + return mockRepo; + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Mocks/MockUnitOfWork.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Mocks/MockUnitOfWork.cs new file mode 100644 index 000000000..d55777d12 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Mocks/MockUnitOfWork.cs @@ -0,0 +1,18 @@ +using CineFlex.Application.Contracts.Persistence; +using Moq; + +namespace BlogApp.Tests.Mocks +{ + public static class MockUnitOfWork + { + public static Mock GetUnitOfWork() + { + var mockUow = new Mock(); + var mockSeatRepo = MockSeatRepository.GetBlogRepository(); + mockUow.Setup(r => r.SeatRepository).Returns(mockSeatRepo.Object); + mockUow.Setup(r => r.Save()).ReturnsAsync(1); + return mockUow; + } + } +} + diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Seat/Command/CreateBlogCommandHandlerTest.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Seat/Command/CreateBlogCommandHandlerTest.cs new file mode 100644 index 000000000..ba2fab7af --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Seat/Command/CreateBlogCommandHandlerTest.cs @@ -0,0 +1,69 @@ +using AutoMapper; +using Shouldly; +using Moq; +using Xunit; +using BlogApp.Tests.Mocks; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.CQRS.Handlers.Commands; +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Profiles; + +namespace BlogApp.Tests.Blog.Command; + +public class CreateBlogCommandHandlerTest +{ + private IMapper _mapper { get; set; } + private Mock _mockUnitOfWork { get; set; } + private CreateSeatCommandHandler _handler { get; set; } + + + public CreateBlogCommandHandlerTest() + { + _mockUnitOfWork = MockUnitOfWork.GetUnitOfWork(); + + _mapper = new MapperConfiguration(c => + { + c.AddProfile(); + }).CreateMapper(); + + _handler = new CreateSeatCommandHandler(_mockUnitOfWork.Object, _mapper); + } + + + [Fact] + public async Task CreateBlogValid() + { + + CreateSeatDto createBlogDto = new() + { + Movie = 1, + Cinema = 1, + Location = "dkjsd" + }; + + // var result = await _handler.Handle(new CreateBlogCommand() { CreateBlogDto = createBlogDto }, CancellationToken.None); + // + // result.Value.Content.ShouldBeEquivalentTo(createBlogDto.Content); + // result.Value.Title.ShouldBeEquivalentTo(createBlogDto.Title); + // + // (await _mockUnitOfWork.Object.BlogRepository.GetAll()).Count.ShouldBe(3); + } + + // [Fact] + // public async Task CreateBlogInvalid() + // { + // + // CreateBlogDto createBlogDto = new() + // { + // Title = "", // Title can't be empty + // Content = "Body of the new blog", + // Publish = true, + // }; + // + // var result = await _handler.Handle(new CreateBlogCommand() { CreateBlogDto = createBlogDto }, CancellationToken.None); + // + // result.Value.ShouldBe(null); + // } +} + + diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Seat/Query/GetBlogDetailsQuery.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Seat/Query/GetBlogDetailsQuery.cs new file mode 100644 index 000000000..390f34105 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application.UnitTest/Seat/Query/GetBlogDetailsQuery.cs @@ -0,0 +1,49 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.CQRS.Handlers.Queries; +using CineFlex.Application.Features.Seats.CQRS.Requests.Queries; +using CineFlex.Application.Profiles; +using AutoMapper; +using Shouldly; +using Moq; +using BlogApp.Tests.Mocks; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.CQRS.Handlers.Queries; +using CineFlex.Application.Profiles; +using Xunit; + +namespace BlogApp.Tests.Blog.Query; + +public class GetBlogDetailsQueryHandlerTest +{ + private IMapper _mapper { get; set; } + private Mock _mockUnitOfWork{ get; set; } + private GetSeatDetailQuerysHandler _handler { get; set; } + + public GetBlogDetailsQueryHandlerTest() + { + _mockUnitOfWork = MockUnitOfWork.GetUnitOfWork(); + + _mapper = new MapperConfiguration(c => + { + c.AddProfile(); + }).CreateMapper(); + + _handler = new GetSeatDetailQuerysHandler(_mockUnitOfWork.Object, _mapper); + } + + [Fact] + public async Task GetBlogDetailsValid() + { + var result = await _handler.Handle(new GetSeatDetailsQuery() { Id = 1}, CancellationToken.None); + result.ShouldNotBe(null); + } + + [Fact] + public async Task GetBlogDetailsInvalid() + { + var result = await _handler.Handle(new GetSeatDetailsQuery() { Id = 100}, CancellationToken.None); + result.Value.ShouldBe(null); + } +} + + diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/CineFlex.Application.csproj b/Backend/backend_assessment/CineFlex/CineFlex.Application/CineFlex.Application.csproj index 117b85cd5..d9585d1a8 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Application/CineFlex.Application.csproj +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/CineFlex.Application.csproj @@ -26,6 +26,11 @@ + + + + + diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IBookingRepository.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IBookingRepository.cs new file mode 100644 index 000000000..ffb0887be --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IBookingRepository.cs @@ -0,0 +1,8 @@ +using CineFlex.Domain; + +namespace CineFlex.Application.Contracts.Persistence; + +public interface IBookingRepository: IGenericRepository +{ + +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/ISeatRepository.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/ISeatRepository.cs new file mode 100644 index 000000000..ea6004f42 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/ISeatRepository.cs @@ -0,0 +1,8 @@ +using CineFlex.Domain; + +namespace CineFlex.Application.Contracts.Persistence; + +public interface ISeatRepository: IGenericRepository +{ + Task> GetAll(int movie, int cinema); +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IUnitOfWork.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IUnitOfWork.cs index 60a0724e4..73b4e51ab 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IUnitOfWork.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IUnitOfWork.cs @@ -11,6 +11,8 @@ public interface IUnitOfWork : IDisposable { IMovieRepository MovieRepository { get; } ICinemaRepository CinemaRepository { get; } + ISeatRepository SeatRepository { get; } + IBookingRepository BookingRepository { get; } Task Save(); } diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Handlers/Commands/DeleteBookingCommandHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Handlers/Commands/DeleteBookingCommandHandler.cs new file mode 100644 index 000000000..8848ca62f --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Handlers/Commands/DeleteBookingCommandHandler.cs @@ -0,0 +1,47 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Bookings.CQRS.Requests.Commands; +using CineFlex.Application.Features.Bookings.DTOs; +using CineFlex.Application.Features.Bookings.DTOs.Validators; +using CineFlex.Application.Responses; +using MediatR; + +namespace CineFlex.Application.Features.Bookings.CQRS.Handlers.Commands; + +public class DeleteBookingCommandHandler: IRequestHandler> +{ + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public DeleteBookingCommandHandler(IUnitOfWork unitOfWork, IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task> Handle(DeleteBookingCommand request, CancellationToken cancellationToken) + { + var response = new BaseCommandResponse(); + var validator = new DeleteBookingCommandValidator(_unitOfWork); + var validationResult = await validator.ValidateAsync(request); + + if (validationResult.IsValid == false) + { + response.Success = false; + response.Message = "Booking deletion Failed"; + response.Errors = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + } + else + { + var booking = await _unitOfWork.BookingRepository.Get(request.Id); + await _unitOfWork.BookingRepository.Delete(booking); + await _unitOfWork.Save(); + + response.Success = true; + response.Message = "Deletion Successful"; + response.Value = _mapper.Map(booking); + } + + return response; + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Handlers/CreateBookingCommandHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Handlers/CreateBookingCommandHandler.cs new file mode 100644 index 000000000..e46660c37 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Handlers/CreateBookingCommandHandler.cs @@ -0,0 +1,65 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Bookings.CQRS.Requests.Commands; +using CineFlex.Application.Features.Bookings.DTOs; +using CineFlex.Application.Features.Bookings.DTOs.Validators; +using CineFlex.Application.Features.Seats.CQRS.Requests.Commands; +using CineFlex.Application.Features.Seats.DTOs.Validators; +using CineFlex.Application.Responses; +using CineFlex.Domain; +using MediatR; + +namespace CineFlex.Application.Features.Bookings.CQRS.Handlers; + +public class CreateBookingCommandHandler: IRequestHandler> +{ + + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public CreateBookingCommandHandler(IUnitOfWork unitOfWork, IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task> Handle(CreateBookingCommand request, CancellationToken cancellationToken) + { + var response = new BaseCommandResponse(); + var validator = new CreateBookingDtoValidator(_unitOfWork); + var validationResult = await validator.ValidateAsync(request.CreateBookingDto); + + if (validationResult.IsValid == false) + { + response.Success = false; + response.Message = "Booking Creation Failed"; + response.Errors = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + } + else + { + var seat = await _unitOfWork.SeatRepository.Get(request.CreateBookingDto.Seat); + seat.Taken = true; + var booking = await _unitOfWork.BookingRepository.Add(new Booking() + { + Seat = seat + }); + + if (await _unitOfWork.Save() > 0) + { + response.Success = true; + response.Message = "Creation Successful"; + response.Value = _mapper.Map(booking); + } + else + { + response.Success = false; + response.Message = "Creation Failed"; + } + + } + + return response; + } + + +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Handlers/Queries/GetBookingDetailQueryHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Handlers/Queries/GetBookingDetailQueryHandler.cs new file mode 100644 index 000000000..2e83c9037 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Handlers/Queries/GetBookingDetailQueryHandler.cs @@ -0,0 +1,48 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Bookings.CQRS.Requests.Queries; +using CineFlex.Application.Features.Bookings.DTOs; +using CineFlex.Application.Features.Bookings.DTOs.Validators; +using CineFlex.Application.Responses; +using CineFlex.Domain; +using MediatR; + +namespace CineFlex.Application.Features.Bookings.CQRS.Handlers.Queries; + +public class GetBookingDetailQueryHandler: IRequestHandler> +{ + + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public GetBookingDetailQueryHandler(IUnitOfWork unitOfWork, IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task> Handle(GetBookingDetailQuery request, CancellationToken cancellationToken) + { + + var response = new BaseCommandResponse(); + var validator = new GetBookingDetailQueryValidator(_unitOfWork); + var validationResult = await validator.ValidateAsync(request); + + if (validationResult.IsValid == false) + { + response.Success = false; + response.Message = "Booking Creation Failed"; + response.Errors = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + } + else + { + var booking = await _unitOfWork.BookingRepository.Get(request.Id); + + response.Success = true; + response.Message = "Creation Successful"; + response.Value = _mapper.Map(booking); + } + + return response; + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Requests/Commands/CreateBookingCommand.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Requests/Commands/CreateBookingCommand.cs new file mode 100644 index 000000000..0ca55d075 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Requests/Commands/CreateBookingCommand.cs @@ -0,0 +1,10 @@ +using CineFlex.Application.Features.Bookings.DTOs; +using CineFlex.Application.Responses; +using MediatR; + +namespace CineFlex.Application.Features.Bookings.CQRS.Requests.Commands; + +public class CreateBookingCommand: IRequest> +{ + public CreateBookingDto CreateBookingDto { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Requests/Commands/DeleteBookingCommand.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Requests/Commands/DeleteBookingCommand.cs new file mode 100644 index 000000000..fbae12e1d --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Requests/Commands/DeleteBookingCommand.cs @@ -0,0 +1,10 @@ +using CineFlex.Application.Features.Bookings.DTOs; +using CineFlex.Application.Responses; +using MediatR; + +namespace CineFlex.Application.Features.Bookings.CQRS.Requests.Commands; + +public class DeleteBookingCommand: IRequest> +{ + public int Id { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Requests/Queries/GetBookingDetailQuery.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Requests/Queries/GetBookingDetailQuery.cs new file mode 100644 index 000000000..4873c25e8 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/CQRS/Requests/Queries/GetBookingDetailQuery.cs @@ -0,0 +1,10 @@ +using CineFlex.Application.Features.Bookings.DTOs; +using CineFlex.Application.Responses; +using MediatR; + +namespace CineFlex.Application.Features.Bookings.CQRS.Requests.Queries; + +public class GetBookingDetailQuery: IRequest> +{ + public int Id { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/BookingDetailDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/BookingDetailDto.cs new file mode 100644 index 000000000..b6554b289 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/BookingDetailDto.cs @@ -0,0 +1,8 @@ +namespace CineFlex.Application.Features.Bookings.DTOs; + +public class BookingDetailDto +{ + public int Id { get; set; } + public int Seat { get; set; } + public int User { get; set; } = 0; +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/CreateBookingDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/CreateBookingDto.cs new file mode 100644 index 000000000..994f624ed --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/CreateBookingDto.cs @@ -0,0 +1,6 @@ +namespace CineFlex.Application.Features.Bookings.DTOs; + +public class CreateBookingDto +{ + public int Seat { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/Validators/CreateBookingDtoValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/Validators/CreateBookingDtoValidator.cs new file mode 100644 index 000000000..4d392c608 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/Validators/CreateBookingDtoValidator.cs @@ -0,0 +1,18 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.DTOs; +using FluentValidation; + +namespace CineFlex.Application.Features.Bookings.DTOs.Validators; + + +public class CreateBookingDtoValidator: AbstractValidator +{ + public CreateBookingDtoValidator(IUnitOfWork unitOfWork) + { + RuleFor(m => m.Seat) + .MustAsync(async (id, token) => await unitOfWork.SeatRepository.Exists(id)).WithMessage($"Seat not found"); + + RuleFor(m => m.Seat) + .MustAsync(async (id, token) => (await unitOfWork.SeatRepository.Get(id)).Taken == false).WithMessage($"Seat Taken"); + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/Validators/DeleteBookingCommandValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/Validators/DeleteBookingCommandValidator.cs new file mode 100644 index 000000000..f223e2f99 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/Validators/DeleteBookingCommandValidator.cs @@ -0,0 +1,16 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Bookings.CQRS.Requests.Commands; +using CineFlex.Application.Features.Bookings.CQRS.Requests.Queries; +using FluentValidation; + +namespace CineFlex.Application.Features.Bookings.DTOs.Validators; + +public class DeleteBookingCommandValidator: AbstractValidator +{ + public DeleteBookingCommandValidator(IUnitOfWork unitOfWork) + { + RuleFor(m => m.Id) + .MustAsync(async (id, token) => await unitOfWork.BookingRepository.Exists(id)).WithMessage($"Booking not found"); + + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/Validators/GetBookingDetailQueryValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/Validators/GetBookingDetailQueryValidator.cs new file mode 100644 index 000000000..c1fbfa7fc --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTOs/Validators/GetBookingDetailQueryValidator.cs @@ -0,0 +1,15 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Bookings.CQRS.Requests.Queries; +using FluentValidation; + +namespace CineFlex.Application.Features.Bookings.DTOs.Validators; + +public class GetBookingDetailQueryValidator: AbstractValidator +{ + public GetBookingDetailQueryValidator(IUnitOfWork unitOfWork) + { + RuleFor(m => m.Id) + .MustAsync(async (id, token) => await unitOfWork.BookingRepository.Exists(id)).WithMessage($"Booking not found"); + + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Movies/CQRS/Handlers/CreateMovieCommandHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Movies/CQRS/Handlers/CreateMovieCommandHandler.cs index d06585f46..066225267 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Movies/CQRS/Handlers/CreateMovieCommandHandler.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Movies/CQRS/Handlers/CreateMovieCommandHandler.cs @@ -31,14 +31,11 @@ public async Task> Handle(CreateMovieCommand request, C var validator = new CreateMovieDtoValidator(); var validationResult = await validator.ValidateAsync(request.MovieDto); - - if (validationResult.IsValid == false) { response.Success = false; response.Message = "Movie Creation Failed"; response.Errors = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); - } else { diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Movies/CQRS/Handlers/UpdateMovieCommandHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Movies/CQRS/Handlers/UpdateMovieCommandHandler.cs index a8c1e32fa..0c2b32e69 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Movies/CQRS/Handlers/UpdateMovieCommandHandler.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Movies/CQRS/Handlers/UpdateMovieCommandHandler.cs @@ -25,7 +25,6 @@ public UpdateMovieCommandHandler(IUnitOfWork unitOfWork, IMapper mapper) public async Task> Handle(UpdateMovieCommand request, CancellationToken cancellationToken) { - var response = new BaseCommandResponse(); diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Commands/CreateSeatCommandHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Commands/CreateSeatCommandHandler.cs new file mode 100644 index 000000000..375b22cec --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Commands/CreateSeatCommandHandler.cs @@ -0,0 +1,63 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.CQRS.Requests.Commands; +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Features.Seats.DTOs.Validators; +using CineFlex.Application.Responses; +using CineFlex.Domain; +using MediatR; + +namespace CineFlex.Application.Features.Seats.CQRS.Handlers.Commands; + +public class CreateSeatCommandHandler: IRequestHandler> +{ + + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public CreateSeatCommandHandler(IUnitOfWork unitOfWork, IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task> Handle(CreateSeatCommand request, CancellationToken cancellationToken) + { + var response = new BaseCommandResponse(); + var validator = new CreateSeatDtoValidator(_unitOfWork); + var validationResult = await validator.ValidateAsync(request.CreateSeatDto); + + if (validationResult.IsValid == false) + { + response.Success = false; + response.Message = "Movie Creation Failed"; + response.Errors = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + } + else + { + var movie = await _unitOfWork.MovieRepository.Get(request.CreateSeatDto.Movie); + var cinema = await _unitOfWork.CinemaRepository.Get(request.CreateSeatDto.Cinema); + var seat = await _unitOfWork.SeatRepository.Add(new Seat() + { + Movie = movie, + Cinema = cinema, + Location = request.CreateSeatDto.Location + }); + + if (await _unitOfWork.Save() > 0) + { + response.Success = true; + response.Message = "Creation Successful"; + response.Value = seat.Id; + } + else + { + response.Success = false; + response.Message = "Creation Failed"; + } + + } + + return response; + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Commands/DeleteSeatCommandHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Commands/DeleteSeatCommandHandler.cs new file mode 100644 index 000000000..1f0fb09a8 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Commands/DeleteSeatCommandHandler.cs @@ -0,0 +1,47 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.CQRS.Requests.Commands; +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Features.Seats.DTOs.Validators; +using CineFlex.Application.Responses; +using CineFlex.Domain; +using MediatR; + +namespace CineFlex.Application.Features.Seats.CQRS.Handlers.Commands; + +public class DeleteSeatCommandHandler: IRequestHandler> +{ + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public DeleteSeatCommandHandler(IUnitOfWork unitOfWork, IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task> Handle(DeleteSeatCommand request, CancellationToken cancellationToken) + { + + var response = new BaseCommandResponse(); + var validator = new DeleteSeatCommandValidator(_unitOfWork); + var validationResult = await validator.ValidateAsync(request); + + if (validationResult.IsValid == false) + { + response.Success = false; + response.Message = "Seat Delete Failed"; + response.Errors = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + } + else + { + response.Success = true; + response.Message = "Delete Successful"; + var seat = await _unitOfWork.SeatRepository.Get(request.Id); + await _unitOfWork.SeatRepository.Delete(seat); + response.Value = _mapper.Map(seat); + } + + return response; + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Commands/UpdateSeatCommandHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Commands/UpdateSeatCommandHandler.cs new file mode 100644 index 000000000..799a8c0ab --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Commands/UpdateSeatCommandHandler.cs @@ -0,0 +1,49 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Movies.DTOs.Validators; +using CineFlex.Application.Features.Seats.CQRS.Requests.Commands; +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Features.Seats.DTOs.Validators; +using CineFlex.Application.Responses; +using MediatR; + +namespace CineFlex.Application.Features.Seats.CQRS.Handlers.Commands; + +public class UpdateSeatCommandHandler: IRequestHandler> +{ + + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public UpdateSeatCommandHandler(IUnitOfWork unitOfWork, IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task> Handle(UpdateSeatCommand request, CancellationToken cancellationToken) + { + var response = new BaseCommandResponse(); + var validator = new UpdateSeatDtoValidator(_unitOfWork); + var validationResult = await validator.ValidateAsync(request.UpdateSeatDto); + + if (validationResult.IsValid == false) + { + response.Success = false; + response.Message = "Seat Update Failed"; + response.Errors = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + } + else + { + response.Success = true; + response.Message = "Update Successful"; + var seat = await _unitOfWork.SeatRepository.Get(request.UpdateSeatDto.Id); + seat.Taken = request.UpdateSeatDto.Taken; + await _unitOfWork.SeatRepository.Update(seat); + await _unitOfWork.Save(); + response.Value = _mapper.Map(seat); + } + + return response; + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Queries/GetAllSeatsQueryHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Queries/GetAllSeatsQueryHandler.cs new file mode 100644 index 000000000..e4be00f53 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Queries/GetAllSeatsQueryHandler.cs @@ -0,0 +1,48 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.CQRS.Requests.Queries; +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Features.Seats.DTOs.Validators; +using CineFlex.Application.Responses; +using CineFlex.Domain; +using MediatR; + +namespace CineFlex.Application.Features.Seats.CQRS.Handlers.Queries; + +public class GetAllSeatsQueryHandler: IRequestHandler?>> +{ + + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public GetAllSeatsQueryHandler(IUnitOfWork unitOfWork, IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task?>> Handle(GetAllSeatsQuery request, CancellationToken cancellationToken) + { + + var response = new BaseCommandResponse?>(); + var validator = new GetAllSeatsDtoValidator(_unitOfWork); + var validationResult = await validator.ValidateAsync(request.GetAllSeatsDto); + + if (validationResult.IsValid == false) + { + response.Success = false; + response.Message = "Seat Fetching Failed"; + response.Errors = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + } + else + { + var seats = await _unitOfWork.SeatRepository.GetAll(request.GetAllSeatsDto.Movie, request.GetAllSeatsDto.Cinema); + + response.Success = true; + response.Message = "Fetch Successful"; + response.Value = _mapper.Map>(seats); + } + + return response; + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Queries/GetSeatDetailQuerysHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Queries/GetSeatDetailQuerysHandler.cs new file mode 100644 index 000000000..252126d7d --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/Queries/GetSeatDetailQuerysHandler.cs @@ -0,0 +1,47 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.CQRS.Requests.Queries; +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Features.Seats.DTOs.Validators; +using CineFlex.Application.Responses; +using CineFlex.Domain; +using MediatR; + +namespace CineFlex.Application.Features.Seats.CQRS.Handlers.Queries; + +public class GetSeatDetailQuerysHandler: IRequestHandler> +{ + private readonly IUnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public GetSeatDetailQuerysHandler(IUnitOfWork unitOfWork, IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task> Handle(GetSeatDetailsQuery request, CancellationToken cancellationToken) + { + + var response = new BaseCommandResponse(); + var validator = new GetSeatDetailsQueryValidator(_unitOfWork); + var validationResult = await validator.ValidateAsync(request); + + if (validationResult.IsValid == false) + { + response.Success = false; + response.Message = "Seat search Failed"; + response.Errors = validationResult.Errors.Select(e => e.ErrorMessage).ToList(); + } + else + { + response.Success = true; + response.Message = "Fetch Successful"; + var seat = await _unitOfWork.SeatRepository.Get(request.Id); + response.Value = _mapper.Map(seat); + } + + return response; + + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Commands/CreateSeatCommand.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Commands/CreateSeatCommand.cs new file mode 100644 index 000000000..dbb9c1921 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Commands/CreateSeatCommand.cs @@ -0,0 +1,10 @@ +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Responses; +using MediatR; + +namespace CineFlex.Application.Features.Seats.CQRS.Requests.Commands; + +public class CreateSeatCommand: IRequest> +{ + public CreateSeatDto CreateSeatDto { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Commands/DeleteSeatCommand.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Commands/DeleteSeatCommand.cs new file mode 100644 index 000000000..8693768bb --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Commands/DeleteSeatCommand.cs @@ -0,0 +1,11 @@ +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Responses; +using CineFlex.Domain; +using MediatR; + +namespace CineFlex.Application.Features.Seats.CQRS.Requests.Commands; + +public class DeleteSeatCommand: IRequest> +{ + public int Id { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Commands/UpdateSeatCommand.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Commands/UpdateSeatCommand.cs new file mode 100644 index 000000000..96ba6a404 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Commands/UpdateSeatCommand.cs @@ -0,0 +1,10 @@ +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Responses; +using MediatR; + +namespace CineFlex.Application.Features.Seats.CQRS.Requests.Commands; + +public class UpdateSeatCommand: IRequest> +{ + public UpdateSeatDto UpdateSeatDto { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Queries/GetAllSeatsQuery.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Queries/GetAllSeatsQuery.cs new file mode 100644 index 000000000..6041b8f49 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Queries/GetAllSeatsQuery.cs @@ -0,0 +1,10 @@ +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Responses; +using MediatR; + +namespace CineFlex.Application.Features.Seats.CQRS.Requests.Queries; + +public class GetAllSeatsQuery: IRequest?>> +{ + public GetAllSeatsDto GetAllSeatsDto { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Queries/GetSeatDetailsQuery.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Queries/GetSeatDetailsQuery.cs new file mode 100644 index 000000000..0766f6d5c --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Requests/Queries/GetSeatDetailsQuery.cs @@ -0,0 +1,10 @@ +using CineFlex.Application.Features.Seats.DTOs; +using CineFlex.Application.Responses; +using MediatR; + +namespace CineFlex.Application.Features.Seats.CQRS.Requests.Queries; + +public class GetSeatDetailsQuery: IRequest> +{ + public int Id { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/CreateSeatDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/CreateSeatDto.cs new file mode 100644 index 000000000..fc8639126 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/CreateSeatDto.cs @@ -0,0 +1,8 @@ +namespace CineFlex.Application.Features.Seats.DTOs; + +public class CreateSeatDto +{ + public int Movie { get; set; } + public int Cinema { get; set; } + public string Location { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/GetAllSeatsDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/GetAllSeatsDto.cs new file mode 100644 index 000000000..244d862c8 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/GetAllSeatsDto.cs @@ -0,0 +1,7 @@ +namespace CineFlex.Application.Features.Seats.DTOs; + +public class GetAllSeatsDto +{ + public int Movie { get; set; } + public int Cinema { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/SeatDetailsDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/SeatDetailsDto.cs new file mode 100644 index 000000000..b8e297533 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/SeatDetailsDto.cs @@ -0,0 +1,10 @@ +namespace CineFlex.Application.Features.Seats.DTOs; + +public class SeatDetailsDto +{ + public int Id { get; set; } + public int Movie { get; set; } + public int Cinema { get; set; } + public string Location { get; set; } + public bool Taken { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/UpdateSeatDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/UpdateSeatDto.cs new file mode 100644 index 000000000..8a36a7453 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/UpdateSeatDto.cs @@ -0,0 +1,7 @@ +namespace CineFlex.Application.Features.Seats.DTOs; + +public class UpdateSeatDto +{ + public int Id { get; set; } + public bool Taken { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/CreateSeatDtoValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/CreateSeatDtoValidator.cs new file mode 100644 index 000000000..316e8f662 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/CreateSeatDtoValidator.cs @@ -0,0 +1,17 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Movies.DTOs; +using FluentValidation; + +namespace CineFlex.Application.Features.Seats.DTOs.Validators; + +public class CreateSeatDtoValidator: AbstractValidator +{ + public CreateSeatDtoValidator(IUnitOfWork unitOfWork) + { + RuleFor(m => m.Movie) + .MustAsync(async (id, token) => await unitOfWork.MovieRepository.Exists(id)).WithMessage($"Movie not found"); + + RuleFor(m => m.Cinema) + .MustAsync(async (id, token) => await unitOfWork.CinemaRepository.Exists(id)).WithMessage($"Cinema not found"); + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/DeleteSeatCommandValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/DeleteSeatCommandValidator.cs new file mode 100644 index 000000000..5be66ba52 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/DeleteSeatCommandValidator.cs @@ -0,0 +1,14 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.CQRS.Requests.Commands; +using FluentValidation; + +namespace CineFlex.Application.Features.Seats.DTOs.Validators; + +public class DeleteSeatCommandValidator: AbstractValidator +{ + public DeleteSeatCommandValidator(IUnitOfWork unitOfWork) + { + RuleFor(m => m.Id) + .MustAsync(async (id, token) => await unitOfWork.SeatRepository.Exists(id)).WithMessage($"Seat not found"); + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/GetAllSeatsDtoValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/GetAllSeatsDtoValidator.cs new file mode 100644 index 000000000..c977a9cf3 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/GetAllSeatsDtoValidator.cs @@ -0,0 +1,16 @@ +using CineFlex.Application.Contracts.Persistence; +using FluentValidation; + +namespace CineFlex.Application.Features.Seats.DTOs.Validators; + +public class GetAllSeatsDtoValidator: AbstractValidator +{ + public GetAllSeatsDtoValidator(IUnitOfWork unitOfWork) + { + RuleFor(m => m.Movie) + .MustAsync(async (id, token) => await unitOfWork.MovieRepository.Exists(id)).WithMessage($"Movie not found"); + + RuleFor(m => m.Cinema) + .MustAsync(async (id, token) => await unitOfWork.CinemaRepository.Exists(id)).WithMessage($"Cinema not found"); + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/GetSeatDetailsQueryValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/GetSeatDetailsQueryValidator.cs new file mode 100644 index 000000000..32f00b25c --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/GetSeatDetailsQueryValidator.cs @@ -0,0 +1,14 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.CQRS.Requests.Queries; +using FluentValidation; + +namespace CineFlex.Application.Features.Seats.DTOs.Validators; + +public class GetSeatDetailsQueryValidator: AbstractValidator +{ + public GetSeatDetailsQueryValidator(IUnitOfWork unitOfWork) + { + RuleFor(m => m.Id) + .MustAsync(async (id, token) => await unitOfWork.SeatRepository.Exists(id)).WithMessage($"Seat not found"); + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/UpdateSeatDtoValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/UpdateSeatDtoValidator.cs new file mode 100644 index 000000000..c16e9e030 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTOs/Validators/UpdateSeatDtoValidator.cs @@ -0,0 +1,14 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Seats.DTOs; +using FluentValidation; + +namespace CineFlex.Application.Features.Seats.DTOs.Validators; + +public class UpdateSeatDtoValidator: AbstractValidator +{ + public UpdateSeatDtoValidator(IUnitOfWork unitOfWork) + { + RuleFor(m => m.Id) + .MustAsync(async (id, token) => await unitOfWork.SeatRepository.Exists(id)).WithMessage($"Seat not found"); + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Profiles/MappingProfile.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Profiles/MappingProfile.cs index 7c00bdd4c..e471ff193 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Application/Profiles/MappingProfile.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Profiles/MappingProfile.cs @@ -8,11 +8,16 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Features.Bookings.DTOs; +using CineFlex.Application.Features.Seats.DTOs; namespace CineFlex.Application.Profiles { public class MappingProfile : Profile { + private readonly IUnitOfWork _unitOfWork; + public MappingProfile() { #region Movie Mappings @@ -26,6 +31,22 @@ public MappingProfile() CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + + #region Seats + + CreateMap() + .ForMember(dest => dest.Movie, + opt => opt.MapFrom(src => src.Movie.Id)) + .ForMember(dest => dest.Cinema, + opt => opt.MapFrom(src => src.Cinema.Id)); + + #endregion Seats + + + + CreateMap() + .ForMember(dest => dest.Seat, + opt => opt.MapFrom(src => src.Seat.Id)); } } } diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Domain/Booking.cs b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Booking.cs new file mode 100644 index 000000000..56595a7e4 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Booking.cs @@ -0,0 +1,9 @@ +using CineFlex.Domain.Common; + +namespace CineFlex.Domain; + +public class Booking: BaseDomainEntity +{ + // public User User { get; set; } + public Seat Seat { get; set; } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Domain/CineFlex.Domain.csproj b/Backend/backend_assessment/CineFlex/CineFlex.Domain/CineFlex.Domain.csproj index 132c02c59..853e6267c 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Domain/CineFlex.Domain.csproj +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/CineFlex.Domain.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Domain/CinemaEntity.cs b/Backend/backend_assessment/CineFlex/CineFlex.Domain/CinemaEntity.cs index b70624cef..d9c3ccd43 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Domain/CinemaEntity.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/CinemaEntity.cs @@ -7,7 +7,7 @@ namespace CineFlex.Domain { - public class CinemaEntity:BaseDomainEntity + public class CinemaEntity: BaseDomainEntity { public string Name { get; set; } public string Location { get; set; } diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Domain/Movie.cs b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Movie.cs index b6f39be96..5b9d1421b 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Domain/Movie.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Movie.cs @@ -12,6 +12,5 @@ public class Movie: BaseDomainEntity public string Title { get; set; } public string Genre { get; set; } public string ReleaseYear { get; set; } - } } diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Domain/Seat.cs b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Seat.cs new file mode 100644 index 000000000..b490ce14a --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Seat.cs @@ -0,0 +1,11 @@ +using CineFlex.Domain.Common; + +namespace CineFlex.Domain; + +public class Seat: BaseDomainEntity +{ + public Movie Movie { get; set; } + public CinemaEntity Cinema { get; set; } + public string Location { get; set; } + public bool Taken { get; set; } = false; +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Domain/User.cs b/Backend/backend_assessment/CineFlex/CineFlex.Domain/User.cs new file mode 100644 index 000000000..6d1985cf8 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/User.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Identity; + +namespace CineFlex.Domain; + +public class User: IdentityUser +{ + +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlex.Persistence.csproj b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlex.Persistence.csproj index d4438f88f..c598fbcb9 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlex.Persistence.csproj +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlex.Persistence.csproj @@ -12,6 +12,7 @@ + all diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlexDbContex.cs b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlexDbContex.cs index 20ccf56db..8391afb08 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlexDbContex.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlexDbContex.cs @@ -8,6 +8,8 @@ using System.Threading.Tasks; using CineFlex.Domain.Common; using CineFlex.Domain; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; namespace CineFlex.Persistence { @@ -43,6 +45,8 @@ public override Task SaveChangesAsync(CancellationToken cancellationToken = public DbSet Cinemas { get; set; } public DbSet Movies { get; set; } - + + public DbSet Bookings { get; set; } + public DbSet Seats { get; set; } } } diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/BookingRepository.cs b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/BookingRepository.cs new file mode 100644 index 000000000..476205746 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/BookingRepository.cs @@ -0,0 +1,11 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Domain; + +namespace CineFlex.Persistence.Repositories; + +public class BookingRepository: GenericRepository, IBookingRepository +{ + public BookingRepository(CineFlexDbContex dbContext) : base(dbContext) + { + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/SeatRepository.cs b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/SeatRepository.cs new file mode 100644 index 000000000..dd4e179d5 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/SeatRepository.cs @@ -0,0 +1,31 @@ +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Domain; +using CineFlex.Persistence.Repositories; +using Microsoft.EntityFrameworkCore; + +namespace CineFlex.Persistence; + +public class SeatRepository: GenericRepository, ISeatRepository +{ + private readonly CineFlexDbContex _context; + public SeatRepository(CineFlexDbContex dbContext) : base(dbContext) + { + _context = dbContext; + } + + public async Task Get(int id) + { + var withMovies = _context.Seats.Include(b => b.Movie); + var withCinemas = withMovies.Include(b => b.Cinema); + + return await withCinemas.FirstOrDefaultAsync(b => b.Id == id); + } + + public async Task> GetAll(int movie, int cinema) + { + var withMovies = _context.Seats.Include(b => b.Movie); + var withCinemas = withMovies.Include(b => b.Cinema); + + return await withCinemas.Where(b => b.Cinema.Id == cinema && b.Movie.Id == movie).ToListAsync(); + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/UnitOfWork.cs b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/UnitOfWork.cs index 7fdd26456..29433329c 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/UnitOfWork.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/UnitOfWork.cs @@ -13,8 +13,11 @@ public class UnitOfWork : IUnitOfWork { private readonly CineFlexDbContex _context; private IMovieRepository _MovieRepository; - + private ICinemaRepository _cinemaRepository; + private ISeatRepository _seatRepository; + private IBookingRepository _bookingRepository; + public UnitOfWork(CineFlexDbContex context) { _context = context; @@ -29,6 +32,27 @@ public IMovieRepository MovieRepository return _MovieRepository; } } + + public IBookingRepository BookingRepository + { + get + { + if (_bookingRepository == null) + _bookingRepository = new BookingRepository(_context); + return _bookingRepository; + } + } + + public ISeatRepository SeatRepository + { + get + { + if (_seatRepository == null) + _seatRepository = new SeatRepository(_context); + return _seatRepository; + } + } + public ICinemaRepository CinemaRepository { get