diff --git a/App/Modules/Bookings/API/V1/BookingController.cs b/App/Modules/Bookings/API/V1/BookingController.cs index f409b58..e14e447 100644 --- a/App/Modules/Bookings/API/V1/BookingController.cs +++ b/App/Modules/Bookings/API/V1/BookingController.cs @@ -1,6 +1,7 @@ using System.Net.Mime; using App.Error.V1; using App.Modules.Common; +using App.Modules.Timings.API.V1; using App.StartUp.Registry; using App.StartUp.Services.Auth; using App.Utility; @@ -21,6 +22,7 @@ public class BookingController( IBookingService service, CreateBookingReqValidator createBookingReqValidator, BookingSearchQueryValidator bookingSearchQueryValidator, + ReserveBookingQueryValidator reserveBookingQueryValidator, IAuthHelper authHelper, ILogger logger, IBookingImageEnricher enrich @@ -39,6 +41,18 @@ public async Task>> Search([FromQu return this.ReturnResult(x); } + [Authorize(Policy = AuthPolicies.AdminOrTin)] + [HttpGet("reserve/{Direction}/{Date}/{Time}")] + public async Task> Reserve([FromRoute] ReserveBookingQuery query) + { + var book = await reserveBookingQueryValidator.ValidateAsyncResult(query, "Invalid ReserveBookingQuery") + .ThenAwait(q => service.Reserve( + q.Direction.DirectionToDomain(), q.Date.ToDate(), q.Time.ToTime())) + .Then(x => x?.ToRes(), Errors.MapAll) + .ThenAwait(x => Utils.ToNullableTaskResultOr(x, r => enrich.Enrich(r))); + return this.ReturnNullableResult(book, new EntityNotFound("Booking not found", typeof(Booking), $"{query.Direction}-{query.Date}-{query.Time}")); + } + [Authorize, HttpGet("{userId}/{id:guid}")] public async Task> Get(string userId, Guid id) { diff --git a/App/Modules/Bookings/API/V1/BookingModel.cs b/App/Modules/Bookings/API/V1/BookingModel.cs index 9baf229..04965fc 100644 --- a/App/Modules/Bookings/API/V1/BookingModel.cs +++ b/App/Modules/Bookings/API/V1/BookingModel.cs @@ -4,6 +4,8 @@ namespace App.Modules.Bookings.API.V1; public record SearchBookingQuery(string? Date, string? Direction, string? Time, string? UserId, int? Limit, int? Skip); +public record ReserveBookingQuery(string Date, string Direction, string Time); + // REQ public record BookingPassengerReq(string FullName, string Gender, string PassportExpiry, string PassportNumber); diff --git a/App/Modules/Bookings/API/V1/BookingValidator.cs b/App/Modules/Bookings/API/V1/BookingValidator.cs index 2ec2f91..a1d47ae 100644 --- a/App/Modules/Bookings/API/V1/BookingValidator.cs +++ b/App/Modules/Bookings/API/V1/BookingValidator.cs @@ -75,3 +75,17 @@ public BookingSearchQueryValidator() .Skip(); } } + +public class ReserveBookingQueryValidator : AbstractValidator +{ + public ReserveBookingQueryValidator() + { + this.RuleFor(x => x.Date) + .DateValid(); + this.RuleFor(x => x.Time) + .TimeValid(); + this.RuleFor(x => x.Direction) + .NotNull() + .TrainDirectionValid(); + } +} diff --git a/App/Modules/Bookings/Data/BookingRepository.cs b/App/Modules/Bookings/Data/BookingRepository.cs index 8d75d86..cda27ca 100644 --- a/App/Modules/Bookings/Data/BookingRepository.cs +++ b/App/Modules/Bookings/Data/BookingRepository.cs @@ -4,6 +4,7 @@ using App.Utility; using CSharp_Result; using Domain.Booking; +using Domain.Timings; using EntityFramework.Exceptions.Common; using Microsoft.EntityFrameworkCore; @@ -156,6 +157,32 @@ public async Task> Create(string userId, BookingRecord } } + public async Task> Reserve(TrainDirection direction, DateOnly date, TimeOnly time) + { + try + { + logger.LogInformation("Reserve Booking '{direction}' '{Date}' '{Time}'", direction, date, time); + + var v1 = await db.Bookings + .Where(x => + x.Direction == direction.ToData() + && x.Date == date + && x.Time == time + && x.Status == (int)BookStatus.Pending + ) + .OrderBy(x => x.CreatedAt) + .FirstOrDefaultAsync(); + return v1?.ToPrincipal(); + } + + catch (Exception e) + { + logger.LogError(e, "Failed to reserve Booking '{direction}' '{Date}' '{Time}'", direction, + date, time); + return e; + } + } + public async Task> Delete(string? userId, Guid id) { try diff --git a/Domain/Booking/IService.cs b/Domain/Booking/IService.cs index 1dda587..be44328 100644 --- a/Domain/Booking/IService.cs +++ b/Domain/Booking/IService.cs @@ -1,4 +1,5 @@ using CSharp_Result; +using Domain.Timings; namespace Domain.Booking; @@ -12,6 +13,8 @@ public interface IBookingService Task> Update(string? userId, Guid id, BookingRecord record); + Task> Reserve(TrainDirection direction, DateOnly date, TimeOnly time); + Task> Buying(Guid id); Task> Complete(Guid id, Stream ticketFile); diff --git a/Domain/Booking/Model.cs b/Domain/Booking/Model.cs index 579f008..facca53 100644 --- a/Domain/Booking/Model.cs +++ b/Domain/Booking/Model.cs @@ -12,6 +12,8 @@ public enum BookStatus Cancelled = 3, } + + public record BookingSearch { public DateOnly? Date { get; init; } diff --git a/Domain/Booking/Repository.cs b/Domain/Booking/Repository.cs index e46f106..fa39a33 100644 --- a/Domain/Booking/Repository.cs +++ b/Domain/Booking/Repository.cs @@ -1,4 +1,5 @@ using CSharp_Result; +using Domain.Timings; namespace Domain.Booking; @@ -10,7 +11,10 @@ public interface IBookingRepository Task> Create(string userId, BookingRecord record); - Task> Update(string? userId, Guid id, BookingStatus? status, BookingRecord? record, BookingComplete? complete); + Task> Update(string? userId, Guid id, BookingStatus? status, BookingRecord? record, + BookingComplete? complete); + + Task> Reserve(TrainDirection direction, DateOnly date, TimeOnly Time); Task> Delete(string? userId, Guid id); diff --git a/Domain/Booking/Service.cs b/Domain/Booking/Service.cs index b0247c6..f1d0ecd 100644 --- a/Domain/Booking/Service.cs +++ b/Domain/Booking/Service.cs @@ -1,4 +1,5 @@ using CSharp_Result; +using Domain.Timings; using Microsoft.Extensions.Logging; namespace Domain.Booking; @@ -26,6 +27,11 @@ public Task> Create(string userId, BookingRecord record return repo.Update(userId, id, null, record, null); } + public Task> Reserve(TrainDirection direction, DateOnly date, TimeOnly time) + { + return repo.Reserve(direction, date, time); + } + public Task> Buying(Guid id) { return repo.Update(null, id, new BookingStatus() { Status = BookStatus.Buying, CompletedAt = null }, null, null);