diff --git a/Hotels.API/Controllers/BookingsController.cs b/Hotels.API/Controllers/BookingsController.cs new file mode 100644 index 0000000..6ebe4cc --- /dev/null +++ b/Hotels.API/Controllers/BookingsController.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Hotels.DataAccess.Data; +using Hotels.Models.Models; + +namespace Hotels.API.Controllers; + +[Route("api/[controller]")] +[ApiController] +public class BookingsController : ControllerBase +{ + private readonly DataContext _context; + + public BookingsController(DataContext context) + { + _context = context; + } + + // GET: api/Bookings + [HttpGet] + public async Task>> GetBookings() + { + if (_context.Bookings == null) + { + return NotFound(); + } + return await _context.Bookings.ToListAsync(); + } + + // GET: api/Bookings/5 + [HttpGet("{id}")] + public async Task> GetBooking(int id) + { + if (_context.Bookings == null) + { + return NotFound(); + } + var booking = await _context.Bookings.FindAsync(id); + + if (booking == null) + { + return NotFound(); + } + + return booking; + } + + // PUT: api/Bookings/5 + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPut("{id}")] + public async Task PutBooking(int id, Booking booking) + { + if (id != booking.Id) + { + return BadRequest(); + } + + _context.Entry(booking).State = EntityState.Modified; + + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!BookingExists(id)) + { + return NotFound(); + } + else + { + throw; + } + } + + return NoContent(); + } + + // POST: api/Bookings + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPost] + public async Task> PostBooking(Booking booking) + { + if (_context.Bookings == null) + { + return Problem("Entity set 'DataContext.Bookings' is null."); + } + _context.Bookings.Add(booking); + await _context.SaveChangesAsync(); + + return CreatedAtAction("GetBooking", new { id = booking.Id }, booking); + } + + // DELETE: api/Bookings/5 + [HttpDelete("{id}")] + public async Task DeleteBooking(int id) + { + if (_context.Bookings == null) + { + return NotFound(); + } + var booking = await _context.Bookings.FindAsync(id); + if (booking == null) + { + return NotFound(); + } + + _context.Bookings.Remove(booking); + await _context.SaveChangesAsync(); + + return NoContent(); + } + + private bool BookingExists(int id) + { + return (_context.Bookings?.Any(e => e.Id == id)).GetValueOrDefault(); + } +} diff --git a/Hotels.API/Controllers/CitiesController.cs b/Hotels.API/Controllers/CitiesController.cs index 5638d10..38fc47c 100644 --- a/Hotels.API/Controllers/CitiesController.cs +++ b/Hotels.API/Controllers/CitiesController.cs @@ -5,7 +5,7 @@ using AutoMapper; using Hotels.DataAccess.Contracts; using Microsoft.AspNetCore.Authorization; -using Hotels.API.Exceptions; +using Hotels.Models.Exceptions; namespace Hotels.API.Controllers; diff --git a/Hotels.API/Middlewares/ExceptionMiddleware.cs b/Hotels.API/Middlewares/ExceptionMiddleware.cs index 52ab188..e112322 100644 --- a/Hotels.API/Middlewares/ExceptionMiddleware.cs +++ b/Hotels.API/Middlewares/ExceptionMiddleware.cs @@ -1,4 +1,4 @@ -using Hotels.API.Exceptions; +using Hotels.Models.Exceptions; using Newtonsoft.Json; using System.Net; diff --git a/Hotels.DataAccess/Contracts/ICitiesRepository.cs b/Hotels.DataAccess/Contracts/ICitiesRepository.cs index 93fc0c1..250418b 100644 --- a/Hotels.DataAccess/Contracts/ICitiesRepository.cs +++ b/Hotels.DataAccess/Contracts/ICitiesRepository.cs @@ -1,8 +1,9 @@ -using Hotels.Models.Models; +using Hotels.Models.Dtos.City; +using Hotels.Models.Models; namespace Hotels.DataAccess.Contracts; public interface ICitiesRepository : IGenericRepository { - Task GetDetails(int id); + Task GetDetails(int id); } diff --git a/Hotels.DataAccess/Contracts/IGenericRepository.cs b/Hotels.DataAccess/Contracts/IGenericRepository.cs index 5e9dd71..78c3e8a 100644 --- a/Hotels.DataAccess/Contracts/IGenericRepository.cs +++ b/Hotels.DataAccess/Contracts/IGenericRepository.cs @@ -1,11 +1,20 @@ -namespace Hotels.DataAccess.Contracts; +using Hotels.Models; +using Hotels.Models.Models.QueryResponse; +using Hotels.Models.Models.Response; + +namespace Hotels.DataAccess.Contracts; public interface IGenericRepository where T : class { Task GetAsync(int? id); + Task GetAsync(int? id); Task> GetAllAsync(); + Task> GetAllAsync(); + Task> GetAllAsync(QueryParameters queryParameters); Task AddAsync(T entity); + Task AddAsync(TSource source); Task DeleteAsync(int id); Task UpdateAsync(T entity); + Task UpdateAsync(int id, TSource source) where TSource : IBase; Task Exists(int id); } diff --git a/Hotels.DataAccess/Repository/CitiesRepository.cs b/Hotels.DataAccess/Repository/CitiesRepository.cs index 50e999c..41b69b2 100644 --- a/Hotels.DataAccess/Repository/CitiesRepository.cs +++ b/Hotels.DataAccess/Repository/CitiesRepository.cs @@ -1,6 +1,9 @@ using AutoMapper; +using AutoMapper.QueryableExtensions; using Hotels.DataAccess.Contracts; using Hotels.DataAccess.Data; +using Hotels.Models.Dtos.City; +using Hotels.Models.Exceptions; using Hotels.Models.Models; using Microsoft.EntityFrameworkCore; @@ -16,9 +19,18 @@ public CitiesRepository(DataContext context, IMapper mapper) : base(context, map _mapper = mapper; } - public async Task GetDetails(int id) + public async Task GetDetails(int id) { - return await _context.Cities.Include(q => q.Hotels) - .FirstOrDefaultAsync(q => q.Id == id); + //return await _context.Cities.Include(q => q.Hotels) + // .FirstOrDefaultAsync(q => q.Id == id); + + var city = await _context.Cities.Include(q => q.Hotels) + .ProjectTo(_mapper.ConfigurationProvider) + .FirstOrDefaultAsync(q => q.Id == id); + + if (city is null) + throw new NotFoundException(nameof(GetDetails), id); + + return city; } } diff --git a/Hotels.DataAccess/Repository/GenericRepository.cs b/Hotels.DataAccess/Repository/GenericRepository.cs index a2098d3..98e3526 100644 --- a/Hotels.DataAccess/Repository/GenericRepository.cs +++ b/Hotels.DataAccess/Repository/GenericRepository.cs @@ -2,6 +2,11 @@ using Hotels.DataAccess.Contracts; using Hotels.DataAccess.Data; using Microsoft.EntityFrameworkCore; +using Hotels.Models.Models.Response; +using Hotels.Models.Models.QueryResponse; +using AutoMapper.QueryableExtensions; +using Hotels.Models; +using Hotels.Models.Exceptions; namespace Hotels.DataAccess.Repository; @@ -23,9 +28,23 @@ public async Task AddAsync(T entity) return entity; } + public async Task AddAsync(TSource source) + { + var entity = _mapper.Map(source); + + await _context.AddAsync(entity); + await _context.SaveChangesAsync(); + + return _mapper.Map(entity); + } + public async Task DeleteAsync(int id) { var entity = await GetAsync(id); + + if (entity is null) + throw new NotFoundException(typeof(T).Name, id); + _context.Set().Remove(entity); await _context.SaveChangesAsync(); } @@ -41,6 +60,31 @@ public async Task> GetAllAsync() return await _context.Set().ToListAsync(); } + public async Task> GetAllAsync(QueryParameters queryParameters) + { + var totalSize = await _context.Set().CountAsync(); + var items = await _context.Set() + .Skip(queryParameters.StartIndex) + .Take(queryParameters.PageSize) + .ProjectTo(_mapper.ConfigurationProvider) + .ToListAsync(); + + return new PagedResult + { + Items = items, + PageNumber = queryParameters.PageNumber, + RecordNumber = queryParameters.PageSize, + TotalCount = totalSize + }; + } + + public async Task> GetAllAsync() + { + return await _context.Set() + .ProjectTo(_mapper.ConfigurationProvider) + .ToListAsync(); + } + public async Task GetAsync(int? id) { if (id is null) @@ -49,9 +93,35 @@ public async Task GetAsync(int? id) return await _context.Set().FindAsync(id); } + public async Task GetAsync(int? id) + { + var result = await _context.Set().FindAsync(id); + if (result is null) + throw new NotFoundException(typeof(T).Name, id.HasValue ? id : "No Key Provided"); + + return _mapper.Map(result); + } + public async Task UpdateAsync(T entity) { _context.Update(entity); await _context.SaveChangesAsync(); } + + public async Task UpdateAsync(int id, TSource source) where TSource : IBase + { + if (id != source.Id) + { + throw new BadRequestException("Invalid Id used in request"); + } + + var entity = await GetAsync(id); + + if (entity is null) + throw new NotFoundException(typeof(T).Name, id); + + _mapper.Map(source, entity); + _context.Update(entity); + await _context.SaveChangesAsync(); + } } diff --git a/Hotels.API/Exceptions/BadRequestException.cs b/Hotels.Models/Exceptions/BadRequestException.cs similarity index 77% rename from Hotels.API/Exceptions/BadRequestException.cs rename to Hotels.Models/Exceptions/BadRequestException.cs index 9cf783d..52c5e8c 100644 --- a/Hotels.API/Exceptions/BadRequestException.cs +++ b/Hotels.Models/Exceptions/BadRequestException.cs @@ -1,4 +1,4 @@ -namespace Hotels.API.Exceptions; +namespace Hotels.Models.Exceptions; public class BadRequestException : ApplicationException { diff --git a/Hotels.API/Exceptions/NotFoundException.cs b/Hotels.Models/Exceptions/NotFoundException.cs similarity index 81% rename from Hotels.API/Exceptions/NotFoundException.cs rename to Hotels.Models/Exceptions/NotFoundException.cs index e39152d..1ce5dfd 100644 --- a/Hotels.API/Exceptions/NotFoundException.cs +++ b/Hotels.Models/Exceptions/NotFoundException.cs @@ -1,4 +1,4 @@ -namespace Hotels.API.Exceptions; +namespace Hotels.Models.Exceptions; public class NotFoundException : ApplicationException { diff --git a/Hotels.Models/IBase.cs b/Hotels.Models/IBase.cs new file mode 100644 index 0000000..9dbbe6e --- /dev/null +++ b/Hotels.Models/IBase.cs @@ -0,0 +1,6 @@ +namespace Hotels.Models; + +public interface IBase +{ + int Id { get; set; } +}