diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/CineFlex.API.csproj b/Backend/backend_assessment/CineFlex/CineFlex.API/CineFlex.API.csproj index 8471c9b1d..2b06cb521 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.API/CineFlex.API.csproj +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/CineFlex.API.csproj @@ -12,12 +12,14 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/AccountController.cs b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/AccountController.cs new file mode 100644 index 000000000..27b2a6508 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/AccountController.cs @@ -0,0 +1,30 @@ +using CineFlex.Application.Contracts.Identity; +using CineFlex.Application.Models.Identity; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; + +namespace CineFlex.Api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class AccountController : ControllerBase + { + private readonly IAuthService _authenticationService; + public AccountController(IAuthService authenticationService) + { + _authenticationService = authenticationService; + } + + [HttpPost("login")] + public async Task> Login(AuthRequest request) + { + return Ok(await _authenticationService.Login(request)); + } + + [HttpPost("register")] + public async Task> Register(RegistrationRequest request) + { + return Ok(await _authenticationService.Register(request)); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/SeatController.cs b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/SeatController.cs new file mode 100644 index 000000000..712925aa3 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/Controllers/SeatController.cs @@ -0,0 +1,53 @@ +using CineFlex.Application.Features.Seats.CQRS.Commands; +using CineFlex.Application.Features.Seats.CQRS.Queries; +using CineFlex.Application.Features.Seats.DTO; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; + +namespace CineFlex.API.Controllers +{ + [Route("api/[Controller]")] + [ApiController] + public class SeatController : BaseApiController + { + private readonly IMediator _mediator; + + public SeatController(IMediator mediator) + { + _mediator = mediator; + } + + + + [HttpGet("{id}")] + public async Task Get(int id) + { + return HandleResult(await _mediator.Send(new GetSeatRequest { 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)); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/Middleware/ExceptionMiddleware.cs b/Backend/backend_assessment/CineFlex/CineFlex.API/Middleware/ExceptionMiddleware.cs new file mode 100644 index 000000000..ab22cabb0 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/Middleware/ExceptionMiddleware.cs @@ -0,0 +1,65 @@ +using CineFlex.Application.Exceptions; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; + +namespace CineFlex.Api.Middleware +{ + public class ExceptionMiddleware + { + private readonly RequestDelegate _next; + public ExceptionMiddleware(RequestDelegate next) + { + _next = next; + } + public async Task InvokeAsync(HttpContext httpContext) + { + try + { + await _next(httpContext); + } + catch (Exception ex) + { + await HandleExceptionAsync(httpContext, ex); + } + } + private Task HandleExceptionAsync(HttpContext context, Exception exception) + { + context.Response.ContentType = "application/json"; + HttpStatusCode statusCode = HttpStatusCode.InternalServerError; + string result = JsonConvert.SerializeObject(new ErrorDeatils + { + ErrorMessage = exception.Message, + ErrorType = "Failure" + }); + + switch (exception) + { + case BadRequestException badRequestException: + statusCode = HttpStatusCode.BadRequest; + break; + case ValidationException validationException: + statusCode = HttpStatusCode.BadRequest; + result = JsonConvert.SerializeObject(validationException.Errors); + break; + case NotFoundException notFoundException: + statusCode = HttpStatusCode.NotFound; + break; + default: + break; + } + + context.Response.StatusCode = (int)statusCode; + return context.Response.WriteAsync(result); + } + } + + public class ErrorDeatils + { + public string ErrorType { get; set; } + public string ErrorMessage { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/Program.cs b/Backend/backend_assessment/CineFlex/CineFlex.API/Program.cs index 4e1afd370..08d82bad3 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.API/Program.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/Program.cs @@ -1,13 +1,18 @@ using CineFlex.Application; using CineFlex.Persistence; +using CineFlex.Identity; using Microsoft.OpenApi.Models; using Microsoft.AspNetCore.Identity; +using CineFlex.Api.Middleware; +using Microsoft.AspNetCore.Cors.Infrastructure; var builder = WebApplication.CreateBuilder(args); // Add services builder.Services.ConfigureApplicationServices(); builder.Services.ConfigurePersistenceServices(builder.Configuration); +builder.Services.ConfigureIdentityServices(builder.Configuration); + builder.Services.AddHttpContextAccessor(); AddSwaggerDoc(builder.Services); builder.Services.AddControllers(); @@ -40,6 +45,7 @@ app.UseHttpsRedirection(); app.UseAuthorization(); +app.UseMiddleware(); app.MapControllers(); diff --git a/Backend/backend_assessment/CineFlex/CineFlex.API/appsettings.json b/Backend/backend_assessment/CineFlex/CineFlex.API/appsettings.json index 2b720c223..92bd8fc7d 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.API/appsettings.json +++ b/Backend/backend_assessment/CineFlex/CineFlex.API/appsettings.json @@ -1,6 +1,7 @@ { "ConnectionStrings": { - "CineFlexConnectionString": "User ID=postgres;Password=1234;Server=localhost;Port=5432;Database=CineFlex;Integrated Security=true;Pooling=true;" + "CineFlexConnectionString": "User ID=postgres;Password=postgres;Server=localhost;Port=5432;Database=CineFlex;Integrated Security=true;Pooling=true;", + "CineFlexIdentityConnectionString": "User ID=postgres;Password=postgres;Server=localhost;Port=5432;Database=CineFlexIdentity;Integrated Security=true;Pooling=true;" }, "Logging": { "LogLevel": { @@ -12,5 +13,12 @@ "compilationOptions": { "emitEntryPoint": true, "preserveCompilationContext": true + }, + "JwtSettings": { + "Key": "2J9JFA9THQTH9AHRHTQ9YAQTJ", + "Issuer": "CineFlexApi", + "Audience": "CineFlexApiUser", + "DurationInMinutes": 60 } + } diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/CineFlex.Application.csproj b/Backend/backend_assessment/CineFlex/CineFlex.Application/CineFlex.Application.csproj index 117b85cd5..6666f2979 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Application/CineFlex.Application.csproj +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/CineFlex.Application.csproj @@ -26,6 +26,13 @@ + + + + + + + diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Identity/IAuthService.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Identity/IAuthService.cs new file mode 100644 index 000000000..11453b182 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Identity/IAuthService.cs @@ -0,0 +1,15 @@ +using CineFlex.Application.Models.Identity; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Contracts.Identity +{ + public interface IAuthService + { + Task Login(AuthRequest request); + Task Register(RegistrationRequest request); + + } +} 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..412d53a0b --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IBookingRepository.cs @@ -0,0 +1,13 @@ +using CineFlex.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Contracts.Persistence +{ + public interface IBookingRepository : IGenericRepository + { + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IGenreRepository.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IGenreRepository.cs new file mode 100644 index 000000000..4639f4ae1 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/IGenreRepository.cs @@ -0,0 +1,13 @@ +using CineFlex.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Contracts.Persistence +{ + public interface IGenreRepository : IGenericRepository + { + } +} 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..28cf3abfc --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Contracts/Persistence/ISeatRepository.cs @@ -0,0 +1,13 @@ +using CineFlex.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Contracts.Persistence +{ + public interface ISeatRepository : IGenericRepository + { + } +} 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..fc3787d03 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,10 @@ public interface IUnitOfWork : IDisposable { IMovieRepository MovieRepository { get; } ICinemaRepository CinemaRepository { get; } + ISeatRepository SeatRepository { get; } + IBookingRepository BookingRepository { get; } + IGenreRepository GenreRepository { get; } + Task Save(); } diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/BookingDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/BookingDto.cs new file mode 100644 index 000000000..d0d189a40 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/BookingDto.cs @@ -0,0 +1,23 @@ +using CineFlex.Application.Features.Common; +using CineFlex.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Bookings.DTO +{ + public class BookingDto : BaseDto, IBookingDto + { + public DateTime BookingTime { get; set; } + public decimal TotalPrice { get; set; } + public int MovieId { get; set; } + public List SeatIds { get; set; } + public string CustomerName { get; set; } + public string CustomerEmail { get; set; } + + + } +} + \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/CreateBookingDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/CreateBookingDto.cs new file mode 100644 index 000000000..9556b0e62 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/CreateBookingDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Bookings.DTO +{ + public class CreateBookingDto + { + public int MovieId { get; set; } + public List SeatIds { get; set; } + public string CustomerName { get; set; } + public string CustomerEmail { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/IBookingDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/IBookingDto.cs new file mode 100644 index 000000000..37781f013 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/IBookingDto.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Bookings.DTO +{ + public interface IBookingDto + { + int Id { get; set; } + DateTime BookingTime { get; set; } + decimal TotalPrice { get; set; } + int MovieId { get; set; } + List SeatIds { get; set; } + string CustomerName { get; set; } + string CustomerEmail { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/UpdateBookingDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/UpdateBookingDto.cs new file mode 100644 index 000000000..a64c09c5d --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Bookings/DTO/UpdateBookingDto.cs @@ -0,0 +1,22 @@ +using CineFlex.Application.Features.Common; +using CineFlex.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Bookings.DTO +{ + public class UpdateBookingDto : BaseDto, IBookingDto + { + public DateTime BookingTime { get; set; } + public decimal TotalPrice { get; set; } + public int MovieId { get; set; } + public List SeatIds { get; set; } + public string CustomerName { get; set; } + public string CustomerEmail { get; set; } + + + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/CreateGenreDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/CreateGenreDto.cs new file mode 100644 index 000000000..0f2d818cc --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/CreateGenreDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Genres.DTO +{ + public class CreateGenreDto + { + public string Name { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/GenreDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/GenreDto.cs new file mode 100644 index 000000000..d4b7e350f --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/GenreDto.cs @@ -0,0 +1,15 @@ +using CineFlex.Application.Features.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Genres.DTO +{ + public class GenreDto : BaseDto + { + public string Name { get; set; } + + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/UpdateGenreDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/UpdateGenreDto.cs new file mode 100644 index 000000000..0b5102121 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/UpdateGenreDto.cs @@ -0,0 +1,15 @@ +using CineFlex.Application.Features.Common; +using CineFlex.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Genres.DTO +{ + public class UpdateGenreDto : BaseDto + { + public string Name { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/Validators/CreateGenreDtoValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/Validators/CreateGenreDtoValidator.cs new file mode 100644 index 000000000..53b2c1960 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/Validators/CreateGenreDtoValidator.cs @@ -0,0 +1,17 @@ +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Genres.DTO.Validators +{ + public class CreateGenreDtoValidator : AbstractValidator + { + public CreateGenreDtoValidator() + { + RuleFor(x => x.Name).NotEmpty().WithMessage("Name is required."); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/Validators/UpdateGenreDtoValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/Validators/UpdateGenreDtoValidator.cs new file mode 100644 index 000000000..3121a122a --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Genres/DTO/Validators/UpdateGenreDtoValidator.cs @@ -0,0 +1,18 @@ +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Genres.DTO.Validators +{ + public class UpdateGenreDtoValidator : AbstractValidator + { + public UpdateGenreDtoValidator() + { + RuleFor(x => x.Id).GreaterThan(0).WithMessage("Id must be greater than 0."); + RuleFor(x => x.Name).NotEmpty().WithMessage("Name is required."); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Commands/CreateSeatCommand.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Commands/CreateSeatCommand.cs new file mode 100644 index 000000000..f7dd7491d --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Commands/CreateSeatCommand.cs @@ -0,0 +1,16 @@ +using CineFlex.Application.Features.Seats.DTO; +using CineFlex.Application.Responses; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.CQRS.Commands +{ + public class CreateSeatCommand : IRequest> + { + public CreateSeatDto CreateSeatDto { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Commands/DeleteSeatCommand.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Commands/DeleteSeatCommand.cs new file mode 100644 index 000000000..7b04d8921 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Commands/DeleteSeatCommand.cs @@ -0,0 +1,16 @@ +using CineFlex.Application.Responses; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.CQRS.Commands +{ + public class DeleteSeatCommand : IRequest> + { + public int Id { get; set; } + + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Commands/UpdateSeatCommand.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Commands/UpdateSeatCommand.cs new file mode 100644 index 000000000..f7691aad8 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Commands/UpdateSeatCommand.cs @@ -0,0 +1,16 @@ +using CineFlex.Application.Features.Seats.DTO; +using CineFlex.Application.Responses; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.CQRS.Commands +{ + public class UpdateSeatCommand : IRequest> + { + public UpdateSeatDto UpdateSeatDto { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/CreateSeatCommandHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/CreateSeatCommandHandler.cs new file mode 100644 index 000000000..cf1dfcc59 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/CreateSeatCommandHandler.cs @@ -0,0 +1,59 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Exceptions; +using CineFlex.Application.Features.Seats.CQRS.Commands; +using CineFlex.Application.Features.Seats.DTO.Validators; +using CineFlex.Application.Responses; +using CineFlex.Domain; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.CQRS.Handlers +{ + public class CreateSeatCommandHandler : IRequestHandler> + { + private readonly IMapper _mapper; + private readonly IUnitOfWork _unitOfWork; + + public CreateSeatCommandHandler(IMapper mapper, IUnitOfWork unitOfWork) + { + _mapper = mapper; + _unitOfWork = unitOfWork; + + } + public async Task> Handle(CreateSeatCommand request, CancellationToken cancellationToken) + { + var validator = new CreateSeatDtoValidator(); + var validationResult = await validator.ValidateAsync(request.CreateSeatDto); + var response = new BaseCommandResponse(); + + if (!validationResult.IsValid) + throw new ValidationException(validationResult); + + var seat= _mapper.Map(request.CreateSeatDto); + + seat = await _unitOfWork.SeatRepository.Add(seat); + + 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; + + + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/DeleteSeatCommandHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/DeleteSeatCommandHandler.cs new file mode 100644 index 000000000..b63c83293 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/DeleteSeatCommandHandler.cs @@ -0,0 +1,57 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Exceptions; +using CineFlex.Application.Features.Seats.CQRS.Commands; +using CineFlex.Application.Features.Seats.DTO.Validators; +using CineFlex.Application.Responses; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.CQRS.Handlers +{ + public class DeleteSeatCommandHandler : IRequestHandler> + { + private readonly IMapper _mapper; + private readonly IUnitOfWork _unitOfWork; + + public DeleteSeatCommandHandler(IMapper mapper, IUnitOfWork unitOfWork) + { + _mapper = mapper; + _unitOfWork = unitOfWork; + + } + public async Task> Handle(DeleteSeatCommand request, CancellationToken cancellationToken) + { + + var response = new BaseCommandResponse(); + + + var seat = await _unitOfWork.SeatRepository.Get(request.Id); + + if (seat == null) + throw new NotFoundException(nameof(seat), request.Id); + + await _unitOfWork.SeatRepository.Delete(seat); + + + if (await _unitOfWork.Save() > 0) + { + response.Success = true; + response.Message = "Update Successful"; + response.Value = Unit.Value; + + } + else + { + response.Success = false; + response.Message = "Update Failed"; + } + + return response; + } + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/GetSeatRequestHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/GetSeatRequestHandler.cs new file mode 100644 index 000000000..59be82976 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/GetSeatRequestHandler.cs @@ -0,0 +1,50 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Exceptions; +using CineFlex.Application.Features.Seats.CQRS.Queries; +using CineFlex.Application.Features.Seats.DTO; +using CineFlex.Application.Features.Seats.DTO.Validators; +using CineFlex.Application.Responses; +using CineFlex.Domain; +using MediatR; +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.CQRS.Handlers +{ + public class GetSeatRequestHandler : IRequestHandler> + { + private readonly IMapper _mapper; + private readonly IUnitOfWork _unitOfWork; + + public GetSeatRequestHandler(IMapper mapper, IUnitOfWork unitOfWork) + { + _mapper = mapper; + _unitOfWork = unitOfWork; + + } + public async Task> Handle(GetSeatRequest request, CancellationToken cancellationToken) + { + + var response = new BaseCommandResponse(); + var seat = await _unitOfWork.SeatRepository.Get(request.Id); + + if (seat == null) { + throw new NotFoundException(nameof(seat), request.Id); + } + + response.Success = true; + response.Message = "Fetch Succesful"; + response.Value = _mapper.Map(seat); + + return response; + + + + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/UpdateSeatCommandHandler.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/UpdateSeatCommandHandler.cs new file mode 100644 index 000000000..889aacb1e --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Handlers/UpdateSeatCommandHandler.cs @@ -0,0 +1,61 @@ +using AutoMapper; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Application.Exceptions; +using CineFlex.Application.Features.Seats.CQRS.Commands; +using CineFlex.Application.Features.Seats.DTO.Validators; +using CineFlex.Application.Responses; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.CQRS.Handlers +{ + public class UpdateSeatCommandHandler : IRequestHandler> + { + private readonly IMapper _mapper; + private readonly IUnitOfWork _unitOfWork; + + public UpdateSeatCommandHandler(IMapper mapper, IUnitOfWork unitOfWork) + { + _mapper = mapper; + _unitOfWork = unitOfWork; + + } + public async Task> Handle(UpdateSeatCommand request, CancellationToken cancellationToken) + { + var validator = new UpdateSeatDtoValidator(); + var validationResult = await validator.ValidateAsync(request.UpdateSeatDto); + var response = new BaseCommandResponse(); + + if (!validationResult.IsValid) + throw new ValidationException(validationResult); + + var seat = await _unitOfWork.SeatRepository.Get(request.UpdateSeatDto.Id); + + if (seat == null) + throw new NotFoundException(nameof(seat), request.UpdateSeatDto.Id); + + _mapper.Map(request.UpdateSeatDto, seat); + + await _unitOfWork.SeatRepository.Update(seat); + + if (await _unitOfWork.Save() > 0) + { + response.Success = true; + response.Message = "Update Successful"; + response.Value = Unit.Value; + + } + else + { + response.Success = false; + response.Message = "Update Failed"; + } + + return response; + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Queries/GetSeatRequest.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Queries/GetSeatRequest.cs new file mode 100644 index 000000000..c4a86e176 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/CQRS/Queries/GetSeatRequest.cs @@ -0,0 +1,16 @@ +using CineFlex.Application.Features.Seats.DTO; +using CineFlex.Application.Responses; +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.CQRS.Queries +{ + public class GetSeatRequest : IRequest> + { + public int Id { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/CreateSeatDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/CreateSeatDto.cs new file mode 100644 index 000000000..49bd8bcc1 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/CreateSeatDto.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.DTO +{ + public class CreateSeatDto + { + public int RowNumber { get; set; } + public int SeatNumber { get; set; } + public string SeatType { get; set; } + public bool Availability { get; set; } + public decimal Price { get; set; } + public int CinemaHallId { get; set; } + public int? BookingId { get; set; } + + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/ISeatDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/ISeatDto.cs new file mode 100644 index 000000000..df249dea9 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/ISeatDto.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.DTO +{ + public interface ISeatDto + { + int RowNumber { get; set; } + int SeatNumber { get; set; } + string SeatType { get; set; } + bool Availability { get; set; } + decimal Price { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/SeatDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/SeatDto.cs new file mode 100644 index 000000000..1dfbf9a69 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/SeatDto.cs @@ -0,0 +1,23 @@ +using CineFlex.Application.Features.Common; +using CineFlex.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.DTO +{ + public class SeatDto : BaseDto, ISeatDto + { + public int RowNumber { get; set; } + public int SeatNumber { get; set; } + public string SeatType { get; set; } + public bool Availability { get; set; } + public decimal Price { get; set; } + public int CinemaHallId { get; set; } + public int? BookingId { get; set; } + + + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/UpdateSeatDto.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/UpdateSeatDto.cs new file mode 100644 index 000000000..8c7893ed5 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/UpdateSeatDto.cs @@ -0,0 +1,23 @@ +using CineFlex.Application.Features.Common; +using CineFlex.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.DTO +{ + public class UpdateSeatDto : BaseDto, ISeatDto + { + public int RowNumber { get; set; } + public int SeatNumber { get; set; } + public string SeatType { get; set; } + public bool Availability { get; set; } + public decimal Price { get; set; } + public int CinemaHallId { get; set; } + public int? BookingId { get; set; } + + + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/Validators/CreateSeatDtoValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/Validators/CreateSeatDtoValidator.cs new file mode 100644 index 000000000..69cb69f52 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/Validators/CreateSeatDtoValidator.cs @@ -0,0 +1,21 @@ +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.DTO.Validators +{ + public class CreateSeatDtoValidator : AbstractValidator + { + public CreateSeatDtoValidator() + { + RuleFor(x => x.RowNumber).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + RuleFor(x => x.SeatNumber).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + RuleFor(x => x.SeatType).NotEmpty().WithMessage("{PropertyName} is required."); + RuleFor(x => x.Price).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + RuleFor(x => x.CinemaHallId).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/Validators/SeatDtoValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/Validators/SeatDtoValidator.cs new file mode 100644 index 000000000..b0d0fa604 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/Validators/SeatDtoValidator.cs @@ -0,0 +1,21 @@ +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.DTO.Validators +{ + public class SeatDtoValidator : AbstractValidator + { + public SeatDtoValidator() + { + RuleFor(x => x.RowNumber).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + RuleFor(x => x.SeatNumber).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + RuleFor(x => x.SeatType).NotEmpty().WithMessage("{PropertyName} is required."); + RuleFor(x => x.Price).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + RuleFor(x => x.CinemaHallId).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/Validators/UpdateSeatDtoValidator.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/Validators/UpdateSeatDtoValidator.cs new file mode 100644 index 000000000..288bfb430 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Features/Seats/DTO/Validators/UpdateSeatDtoValidator.cs @@ -0,0 +1,22 @@ +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Application.Features.Seats.DTO.Validators +{ + public class UpdateSeatDtoValidator : AbstractValidator + { + public UpdateSeatDtoValidator() + { + RuleFor(x => x.Id).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + RuleFor(x => x.RowNumber).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + RuleFor(x => x.SeatNumber).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + RuleFor(x => x.SeatType).NotEmpty().WithMessage("{PropertyName} is required."); + RuleFor(x => x.Price).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + RuleFor(x => x.CinemaHallId).GreaterThan(0).WithMessage("{PropertyName} must be greater than 0."); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/AuthRequest.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/AuthRequest.cs new file mode 100644 index 000000000..a2c4442d8 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/AuthRequest.cs @@ -0,0 +1,8 @@ +namespace CineFlex.Application.Models.Identity +{ + public class AuthRequest + { + public string Email { get; set; } + public string Password { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/AuthResponse.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/AuthResponse.cs new file mode 100644 index 000000000..9d5a9a3de --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/AuthResponse.cs @@ -0,0 +1,10 @@ +namespace CineFlex.Application.Models.Identity +{ + public class AuthResponse + { + public string Id { get; set; } + public string UserName { get; set; } + public string Email { get; set; } + public string Token { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/JwtSettings.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/JwtSettings.cs new file mode 100644 index 000000000..d26819b92 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/JwtSettings.cs @@ -0,0 +1,10 @@ +namespace CineFlex.Application.Models.Identity +{ + public class JwtSettings + { + public string Key { get; set; } + public string Issuer { get; set; } + public string Audience { get; set; } + public double DurationInMinutes { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/RegistrationRequest.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/RegistrationRequest.cs new file mode 100644 index 000000000..d2394525e --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/RegistrationRequest.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; + +namespace CineFlex.Application.Models.Identity +{ + public class RegistrationRequest + { + [Required] + public string FirstName { get; set; } + + [Required] + public string LastName { get; set; } + + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [MinLength(6)] + public string UserName { get; set; } + + [Required] + [MinLength(6)] + public string Password { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/RegistrationResponse.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/RegistrationResponse.cs new file mode 100644 index 000000000..60b4f9f3f --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Models/Identity/RegistrationResponse.cs @@ -0,0 +1,7 @@ +namespace CineFlex.Application.Models.Identity +{ + public class RegistrationResponse + { + public string UserId { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Application/Profiles/MappingProfile.cs b/Backend/backend_assessment/CineFlex/CineFlex.Application/Profiles/MappingProfile.cs index 7c00bdd4c..2b58e3db6 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Application/Profiles/MappingProfile.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Application/Profiles/MappingProfile.cs @@ -8,6 +8,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using CineFlex.Application.Features.Seats.DTO; +using CineFlex.Application.Features.Genres.DTO; +using CineFlex.Application.Features.Bookings.DTO; namespace CineFlex.Application.Profiles { @@ -26,6 +29,22 @@ public MappingProfile() CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + + + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + } } } 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..f169f915b --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Booking.cs @@ -0,0 +1,18 @@ +using CineFlex.Domain.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Domain +{ + public class Booking : BaseDomainEntity + { + public DateTime BookingTime { get; set; } + public decimal TotalPrice { get; set; } + public int MovieId { get; set; } + public Movie Movie { get; set; } + public List Seats { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Domain/CinemaEntity.cs b/Backend/backend_assessment/CineFlex/CineFlex.Domain/CinemaEntity.cs index b70624cef..c4baec47e 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Domain/CinemaEntity.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/CinemaEntity.cs @@ -12,5 +12,6 @@ public class CinemaEntity:BaseDomainEntity public string Name { get; set; } public string Location { get; set; } public string ContactInformation { get; set; } + public List Seats {get; set;} } } diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Domain/Genre.cs b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Genre.cs new file mode 100644 index 000000000..2bf62f63f --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Genre.cs @@ -0,0 +1,16 @@ +using CineFlex.Domain.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Domain +{ + public class Genre : BaseDomainEntity + { + public int Id { get; set; } + public string Name { get; set; } + public List Movies { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Domain/Movie.cs b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Movie.cs index b6f39be96..c56898aef 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Domain/Movie.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Movie.cs @@ -10,8 +10,9 @@ namespace CineFlex.Domain public class Movie: BaseDomainEntity { public string Title { get; set; } - public string Genre { get; set; } + public List Genres { get; set; } public string ReleaseYear { get; set; } + public List Bookings { 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..ef5aff9a1 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Domain/Seat.cs @@ -0,0 +1,22 @@ +using CineFlex.Domain.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Domain +{ + public class Seat : BaseDomainEntity + { + public int RowNumber { get; set; } + public int SeatNumber { get; set; } + public string SeatType { get; set; } + public bool Availability { get; set; } + public decimal Price { get; set; } + public int CinemaHallId { get; set; } + public CinemaEntity Cinema { get; set; } + public int BookingId { get; set; } + public Booking Booking { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Identity/CineFlex.Identity.csproj b/Backend/backend_assessment/CineFlex/CineFlex.Identity/CineFlex.Identity.csproj new file mode 100644 index 000000000..8c0b120a6 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Identity/CineFlex.Identity.csproj @@ -0,0 +1,33 @@ + + + + net6.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Identity/CineFlexDbContextFactory.cs b/Backend/backend_assessment/CineFlex/CineFlex.Identity/CineFlexDbContextFactory.cs new file mode 100644 index 000000000..bde3338d1 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Identity/CineFlexDbContextFactory.cs @@ -0,0 +1,26 @@ +using CineFlex.Identity; +using CineFlex; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using Microsoft.EntityFrameworkCore; + +namespace CineFlex.Persistence +{ + public class CineFlexIdentityDbContextFactory : IDesignTimeDbContextFactory + { + public CineFlexIdentityDbContext CreateDbContext(string[] args) + { + IConfigurationRoot configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build(); + + var builder = new DbContextOptionsBuilder(); + var connectionString = configuration.GetConnectionString("CineFlexIdentityConnectionString"); + + builder.UseNpgsql(connectionString); + + return new CineFlexIdentityDbContext(builder.Options); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Identity/CineFlexIdentityDbContext.cs b/Backend/backend_assessment/CineFlex/CineFlex.Identity/CineFlexIdentityDbContext.cs new file mode 100644 index 000000000..4efd6a427 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Identity/CineFlexIdentityDbContext.cs @@ -0,0 +1,26 @@ +using CineFlex.Identity.Models; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using CineFlex.Identity.Configurations; +using System.Collections.Generic; +using System.Text; + +namespace CineFlex.Identity +{ + public class CineFlexIdentityDbContext : IdentityDbContext + { + public CineFlexIdentityDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ApplyConfiguration(new RoleConfiguration()); + modelBuilder.ApplyConfiguration(new UserConfiguration()); + modelBuilder.ApplyConfiguration(new UserRoleConfiguration()); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Identity/Configurations/RoleConfiguration.cs b/Backend/backend_assessment/CineFlex/CineFlex.Identity/Configurations/RoleConfiguration.cs new file mode 100644 index 000000000..01fbf98a5 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Identity/Configurations/RoleConfiguration.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace CineFlex.Identity.Configurations +{ + public class RoleConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasData( + new IdentityRole + { + Id = "cac43a6e-f7bb-4448-baaf-1add431ccbbf", + Name = "Employee", + NormalizedName = "EMPLOYEE" + }, + new IdentityRole + { + Id = "cbc43a8e-f7bb-4445-baaf-1add431ffbbf", + Name = "Administrator", + NormalizedName = "ADMINISTRATOR" + } + ); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Identity/Configurations/UserConfiguration.cs b/Backend/backend_assessment/CineFlex/CineFlex.Identity/Configurations/UserConfiguration.cs new file mode 100644 index 000000000..2101d742a --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Identity/Configurations/UserConfiguration.cs @@ -0,0 +1,45 @@ +using CineFlex.Identity.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace CineFlex.Identity.Configurations +{ + public class UserConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + var hasher = new PasswordHasher(); + builder.HasData( + new ApplicationUser + { + Id = "8e445865-a24d-4543-a6c6-9443d048cdb9", + Email = "admin@localhost.com", + NormalizedEmail = "ADMIN@LOCALHOST.COM", + FirstName = "System", + LastName = "Admin", + UserName = "admin@localhost.com", + NormalizedUserName = "ADMIN@LOCALHOST.COM", + PasswordHash = hasher.HashPassword(null, "P@ssword1"), + EmailConfirmed = true + }, + new ApplicationUser + { + Id = "9e224968-33e4-4652-b7b7-8574d048cdb9", + Email = "user@localhost.com", + NormalizedEmail = "USER@LOCALHOST.COM", + FirstName = "System", + LastName = "User", + UserName = "user@localhost.com", + NormalizedUserName = "USER@LOCALHOST.COM", + PasswordHash = hasher.HashPassword(null, "P@ssword1"), + EmailConfirmed = true + } + ); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Identity/Configurations/UserRoleConfiguration.cs b/Backend/backend_assessment/CineFlex/CineFlex.Identity/Configurations/UserRoleConfiguration.cs new file mode 100644 index 000000000..872c01820 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Identity/Configurations/UserRoleConfiguration.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace CineFlex.Identity.Configurations +{ + public class UserRoleConfiguration : IEntityTypeConfiguration> + { + public void Configure(EntityTypeBuilder> builder) + { + builder.HasData( + new IdentityUserRole + { + RoleId = "cbc43a8e-f7bb-4445-baaf-1add431ffbbf", + UserId = "8e445865-a24d-4543-a6c6-9443d048cdb9" + }, + new IdentityUserRole + { + RoleId = "cac43a6e-f7bb-4448-baaf-1add431ccbbf", + UserId = "9e224968-33e4-4652-b7b7-8574d048cdb9" + } + ); + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Identity/IdentityServicesRegistration.cs b/Backend/backend_assessment/CineFlex/CineFlex.Identity/IdentityServicesRegistration.cs new file mode 100644 index 000000000..2be95e346 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Identity/IdentityServicesRegistration.cs @@ -0,0 +1,58 @@ +using CineFlex.Application.Contracts.Identity; +using CineFlex.Application.Models.Identity; +using CineFlex.Identity.Models; +using CineFlex.Identity.Services; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace CineFlex.Identity +{ + public static class IdentityServicesRegistration + { + public static IServiceCollection ConfigureIdentityServices(this IServiceCollection services, IConfiguration configuration) + { + services.Configure(configuration.GetSection("JwtSettings")); + + services.AddDbContext(options => + options.UseNpgsql(configuration.GetConnectionString("CineFlexIdentityConnectionString"), + b => b.MigrationsAssembly(typeof(CineFlexIdentityDbContext).Assembly.FullName))); + + services.AddIdentity() + .AddEntityFrameworkStores().AddDefaultTokenProviders(); + + services.AddTransient(); + + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(o => + { + o.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ClockSkew = TimeSpan.Zero, + ValidIssuer = configuration["JwtSettings:Issuer"], + ValidAudience = configuration["JwtSettings:Audience"], + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtSettings:Key"])) + }; + }); + + return services; + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Identity/Models/ApplicationUser.cs b/Backend/backend_assessment/CineFlex/CineFlex.Identity/Models/ApplicationUser.cs new file mode 100644 index 000000000..4962e082a --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Identity/Models/ApplicationUser.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Identity; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CineFlex.Identity.Models +{ + public class ApplicationUser : IdentityUser + { + public string FirstName { get; set; } + public string LastName { get; set; } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Identity/Services/AuthService.cs b/Backend/backend_assessment/CineFlex/CineFlex.Identity/Services/AuthService.cs new file mode 100644 index 000000000..ef507c7ed --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Identity/Services/AuthService.cs @@ -0,0 +1,133 @@ +using CineFlex.Application.Constants; +using CineFlex.Application.Contracts.Identity; +using CineFlex.Application.Models.Identity; +using CineFlex.Identity.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; + +namespace CineFlex.Identity.Services +{ + public class AuthService : IAuthService + { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly JwtSettings _jwtSettings; + + public AuthService(UserManager userManager, + IOptions jwtSettings, + SignInManager signInManager) + { + _userManager = userManager; + _jwtSettings = jwtSettings.Value; + _signInManager = signInManager; + } + + public async Task Login(AuthRequest request) + { + var user = await _userManager.FindByEmailAsync(request.Email); + + if (user == null) + { + throw new Exception($"User with {request.Email} not found."); + } + + var result = await _signInManager.PasswordSignInAsync(user.UserName, request.Password, false, lockoutOnFailure: false); + + if (!result.Succeeded) + { + throw new Exception($"Credentials for '{request.Email} aren't valid'."); + } + + JwtSecurityToken jwtSecurityToken = await GenerateToken(user); + + AuthResponse response = new AuthResponse + { + Id = user.Id, + Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken), + Email = user.Email, + UserName = user.UserName + }; + + return response; + } + + public async Task Register(RegistrationRequest request) + { + var existingUser = await _userManager.FindByNameAsync(request.UserName); + + if (existingUser != null) + { + throw new Exception($"Username '{request.UserName}' already exists."); + } + + var user = new ApplicationUser + { + Email = request.Email, + FirstName = request.FirstName, + LastName = request.LastName, + UserName = request.UserName, + EmailConfirmed = true + }; + + var existingEmail = await _userManager.FindByEmailAsync(request.Email); + + if (existingEmail == null) + { + var result = await _userManager.CreateAsync(user, request.Password); + + if (result.Succeeded) + { + await _userManager.AddToRoleAsync(user, "Employee"); + return new RegistrationResponse() { UserId = user.Id }; + } + else + { + throw new Exception($"{result.Errors}"); + } + } + else + { + throw new Exception($"Email {request.Email } already exists."); + } + } + + private async Task GenerateToken(ApplicationUser user) + { + var userClaims = await _userManager.GetClaimsAsync(user); + var roles = await _userManager.GetRolesAsync(user); + + var roleClaims = new List(); + + for (int i = 0; i < roles.Count; i++) + { + roleClaims.Add(new Claim(ClaimTypes.Role, roles[i])); + } + + var claims = new[] + { + new Claim(JwtRegisteredClaimNames.Sub, user.UserName), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new Claim(JwtRegisteredClaimNames.Email, user.Email), + new Claim(CustomClaimTypes.Uid, user.Id) + } + .Union(userClaims) + .Union(roleClaims); + + var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Key)); + var signingCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256); + + var jwtSecurityToken = new JwtSecurityToken( + issuer: _jwtSettings.Issuer, + audience: _jwtSettings.Audience, + claims: claims, + expires: DateTime.UtcNow.AddMinutes(_jwtSettings.DurationInMinutes), + signingCredentials: signingCredentials); + return jwtSecurityToken; + } + } +} diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlex.Persistence.csproj b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlex.Persistence.csproj index d4438f88f..cab284b46 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlex.Persistence.csproj +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlex.Persistence.csproj @@ -13,6 +13,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlexDbContex.cs b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlexDbContex.cs index 20ccf56db..6e28680cf 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlexDbContex.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/CineFlexDbContex.cs @@ -23,6 +23,31 @@ public CineFlexDbContex(DbContextOptions options) protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(CineFlexDbContex).Assembly); + // Relationship between CinemaEntity and Seat + modelBuilder.Entity() + .HasMany(c => c.Seats) + .WithOne(s => s.Cinema) + .HasForeignKey(s => s.CinemaHallId); + + // Relationship between Movie and Genre + modelBuilder.Entity() + .HasMany(m => m.Genres) + .WithMany(g => g.Movies) + .UsingEntity(j => j.ToTable("MovieGenres")); + + // Relationship between Movie and Booking + modelBuilder.Entity() + .HasMany(m => m.Bookings) + .WithOne(b => b.Movie) + .HasForeignKey(b => b.MovieId); + + // Relationship between Booking and Seat + modelBuilder.Entity() + .HasMany(b => b.Seats) + .WithOne(s => s.Booking) + .HasForeignKey(s => s.BookingId); + + } public override Task SaveChangesAsync(CancellationToken cancellationToken = default) @@ -43,6 +68,9 @@ public override Task SaveChangesAsync(CancellationToken cancellationToken = public DbSet Cinemas { get; set; } public DbSet Movies { get; set; } + public DbSet Seats { get; set; } + public DbSet Genres { get; set; } + public DbSet Bookings { get; set; } } } diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/BookingConfiguration.cs b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/BookingConfiguration.cs new file mode 100644 index 000000000..5987feac9 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/BookingConfiguration.cs @@ -0,0 +1,34 @@ +using CineFlex.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Persistence.Configurations.Seeds +{ + public class BookingConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasData( + new Booking + { + Id = 1, + BookingTime = DateTime.Now, + TotalPrice = 20.0m, + MovieId = 1 + }, + new Booking + { + Id = 2, + BookingTime = DateTime.Now, + TotalPrice = 30.0m, + MovieId = 2 + } + ); + } + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/GenreConfiguration.cs b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/GenreConfiguration.cs new file mode 100644 index 000000000..31b8eb970 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/GenreConfiguration.cs @@ -0,0 +1,30 @@ +using CineFlex.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Persistence.Configurations.Entities +{ + public class GenreConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasData( + new Genre + { + Id = 1, + Name = "Action" + }, + new Genre + { + Id = 2, + Name = "Comedy" + } + ); + } + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/MovieConfiguration.cs b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/MovieConfiguration.cs index aed239b9d..2ebdf0d82 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/MovieConfiguration.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/MovieConfiguration.cs @@ -1,11 +1,11 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; +using CineFlex.Domain; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using CineFlex.Domain; namespace CineFlex.Persistence.Configurations.Entities { @@ -17,21 +17,18 @@ public void Configure(EntityTypeBuilder builder) new Movie { Id = 1, - Title = "Sample Movie", - Genre = "Trailer", + Title = "Sample Movie 1", ReleaseYear = "1999", + }, - - new Movie - { - Id = 2, - Title = "Sample Movie", - Genre = "Sci Fi", - ReleaseYear = "2022", - } - ); ; + new Movie + { + Id = 2, + Title = "Sample Movie 2", + ReleaseYear = "2022", + + } + ); } - - } -} +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/SeatConfiguration.cs b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/SeatConfiguration.cs new file mode 100644 index 000000000..df29f9c7f --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Configurations/Entities/SeatConfiguration.cs @@ -0,0 +1,42 @@ +using CineFlex.Domain; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CineFlex.Persistence.Configurations.Entities +{ + public class SeatConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasData( + new Seat + { + Id = 1, + RowNumber = 1, + SeatNumber = 1, + SeatType = "Standard", + Availability = true, + Price = 10.0m, + CinemaHallId = 1, + BookingId = 1 + }, + new Seat + { + Id = 2, + RowNumber = 1, + SeatNumber = 2, + SeatType = "Standard", + Availability = true, + Price = 10.0m, + CinemaHallId = 1, + BookingId = 2 + } + ); + } + } +} \ No newline at end of file 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..76ee2aa37 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/BookingRepository.cs @@ -0,0 +1,17 @@ +using System; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Domain; + +namespace CineFlex.Persistence.Repositories +{ + public class BookingRepository : GenericRepository, IBookingRepository + { + private readonly CineFlexDbContex _dbContext; + + public BookingRepository(CineFlexDbContex context) : base(context) + { + _dbContext = context; + } + + } +} \ No newline at end of file diff --git a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/GenreRepository.cs b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/GenreRepository.cs new file mode 100644 index 000000000..47ad1d0bd --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/GenreRepository.cs @@ -0,0 +1,19 @@ +using System; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Domain; + +namespace CineFlex.Persistence.Repositories +{ + public class GenreRepository : GenericRepository, IGenreRepository + { + private readonly CineFlexDbContex _dbContext; + + public GenreRepository(CineFlexDbContex context) : base(context) + { + + _dbContext = context; + } + + + } +} \ 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..77d191e34 --- /dev/null +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/SeatRepository.cs @@ -0,0 +1,18 @@ +using System; +using CineFlex.Application.Contracts.Persistence; +using CineFlex.Domain; + +namespace CineFlex.Persistence.Repositories +{ + public class SeatRepository : GenericRepository, ISeatRepository + { + private readonly CineFlexDbContex _dbContext; + + + public SeatRepository(CineFlexDbContex context) : base(context) + { + _dbContext = context; + } + + } +} \ 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..df445cca7 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/UnitOfWork.cs +++ b/Backend/backend_assessment/CineFlex/CineFlex.Persistence/Repositories/UnitOfWork.cs @@ -15,6 +15,9 @@ public class UnitOfWork : IUnitOfWork private IMovieRepository _MovieRepository; private ICinemaRepository _cinemaRepository; + private ISeatRepository _seatRepository; + private IGenreRepository _genreRepository; + private IBookingRepository _bookingRepository; public UnitOfWork(CineFlexDbContex context) { _context = context; @@ -39,6 +42,30 @@ public ICinemaRepository CinemaRepository } } + public ISeatRepository SeatRepository{ + get { + if (_seatRepository == null) + _seatRepository = new SeatRepository(_context); + return _seatRepository; + } + } + + public IGenreRepository GenreRepository{ + get{ + if (_genreRepository == null) + _genreRepository = new GenreRepository(_context); + return _genreRepository; + } + } + + public IBookingRepository BookingRepository{ + get{ + if (_bookingRepository == null) + _bookingRepository = new BookingRepository(_context); + return _bookingRepository; + } + } + public void Dispose() { diff --git a/Backend/backend_assessment/CineFlex/CineFlex.sln b/Backend/backend_assessment/CineFlex/CineFlex.sln index 22cdfded1..e959e512a 100644 --- a/Backend/backend_assessment/CineFlex/CineFlex.sln +++ b/Backend/backend_assessment/CineFlex/CineFlex.sln @@ -7,15 +7,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7B1B95A0-9DB EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F5977BC8-191D-48B9-8833-80091338052F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CineFlex.Domain", "CineFlex.Domain\CineFlex.Domain.csproj", "{50EEE1CA-423A-4F8C-8AEB-8F21104E48F5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CineFlex.Domain", "CineFlex.Domain\CineFlex.Domain.csproj", "{50EEE1CA-423A-4F8C-8AEB-8F21104E48F5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CineFlex.Application.UnitTest", "CineFlex.Application.UnitTest\CineFlex.Application.UnitTest.csproj", "{6C23CA30-5F1B-4733-9298-0221D22BC142}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CineFlex.Application.UnitTest", "CineFlex.Application.UnitTest\CineFlex.Application.UnitTest.csproj", "{6C23CA30-5F1B-4733-9298-0221D22BC142}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CineFlex.Application", "CineFlex.Application\CineFlex.Application.csproj", "{104F9AAF-4CC1-4578-9061-146FF1315A64}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CineFlex.Application", "CineFlex.Application\CineFlex.Application.csproj", "{104F9AAF-4CC1-4578-9061-146FF1315A64}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CineFlex.Persistence", "CineFlex.Persistence\CineFlex.Persistence.csproj", "{37F60AFF-7E17-4EC3-A20A-042C6F59F1D9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CineFlex.Persistence", "CineFlex.Persistence\CineFlex.Persistence.csproj", "{37F60AFF-7E17-4EC3-A20A-042C6F59F1D9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CineFlex.API", "CineFlex.API\CineFlex.API.csproj", "{8D82F498-631C-450B-982F-E7333EB92D7C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CineFlex.API", "CineFlex.API\CineFlex.API.csproj", "{8D82F498-631C-450B-982F-E7333EB92D7C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CineFlex.Identity", "CineFlex.Identity\CineFlex.Identity.csproj", "{4978DB40-C294-4B4E-B747-205C1782F6FA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -43,6 +45,10 @@ Global {8D82F498-631C-450B-982F-E7333EB92D7C}.Debug|Any CPU.Build.0 = Debug|Any CPU {8D82F498-631C-450B-982F-E7333EB92D7C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8D82F498-631C-450B-982F-E7333EB92D7C}.Release|Any CPU.Build.0 = Release|Any CPU + {4978DB40-C294-4B4E-B747-205C1782F6FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4978DB40-C294-4B4E-B747-205C1782F6FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4978DB40-C294-4B4E-B747-205C1782F6FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4978DB40-C294-4B4E-B747-205C1782F6FA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -53,6 +59,7 @@ Global {104F9AAF-4CC1-4578-9061-146FF1315A64} = {7B1B95A0-9DBB-4C5D-82E6-3895BCF66EE8} {37F60AFF-7E17-4EC3-A20A-042C6F59F1D9} = {7B1B95A0-9DBB-4C5D-82E6-3895BCF66EE8} {8D82F498-631C-450B-982F-E7333EB92D7C} = {7B1B95A0-9DBB-4C5D-82E6-3895BCF66EE8} + {4978DB40-C294-4B4E-B747-205C1782F6FA} = {7B1B95A0-9DBB-4C5D-82E6-3895BCF66EE8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {30B8E19C-B56E-4DDD-8243-978C779E3BD4}