Skip to content
Open
58 changes: 58 additions & 0 deletions ParkingLotApi/Controllers/ParkingLotsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using Microsoft.AspNetCore.Mvc;
using ParkingLotApi.Dtos;
using ParkingLotApi.Exceptions;
using ParkingLotApi.Models;
using ParkingLotApi.Services;

namespace ParkingLotApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class ParkingLotsController : ControllerBase
{
private ParkingLotService parkingLotService;

public ParkingLotsController(ParkingLotService parkingLotService)
{
this.parkingLotService = parkingLotService;
}

[HttpPost]
public async Task<ActionResult<ParkingLotDto>> CreateParkingLotAsync([FromBody] ParkingLotDto parkingLotDto)
{
//try

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: remove dead code

//{
return StatusCode(StatusCodes.Status201Created, await parkingLotService.CreateAsync(parkingLotDto));
//}
//catch (InvalidCapacityException ex)
//{
// return BadRequest();
//}
}

[HttpDelete("id")]
public async Task<ActionResult<ParkingLotDto>> DeleteParkingLotAsync(string id)
{
await parkingLotService.DeleteParkingLot(id);
return new NoContentResult();
}

[HttpGet]
public async Task<ActionResult<List<ParkingLot>>> GetParkingLotsByPageInfoAsync(int pageIndex)
{
return await parkingLotService.GetParkingLotByPageInfo(pageIndex);
}

[HttpGet("{id}")]
public async Task<ActionResult<ParkingLot>> GetParkingLotByIdAsync(string id)
{
return await parkingLotService.GetParkingLotById(id);
}

[HttpPut("{id}")]
public async Task<ActionResult<ParkingLot>> UpdateParkingLotAsync(string id, [FromBody] ParkingLotDto parkingLotDto)
{
return await parkingLotService.UpdateParkingLot(id, parkingLotDto);
}
}
}
21 changes: 21 additions & 0 deletions ParkingLotApi/Dtos/ParkingLotDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using ParkingLotApi.Models;

namespace ParkingLotApi.Dtos
{
public class ParkingLotDto
{
public string Name { get; set; }
public int Capacity { get; set; }
public string Location { get; set; }

internal ParkingLot ToEntity()
{
return new ParkingLot()
{
Name = this.Name,
Capacity = this.Capacity,
Location = this.Location
};
}
}
}
6 changes: 6 additions & 0 deletions ParkingLotApi/Exceptions/InvalidCapacityException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ParkingLotApi.Exceptions
{
public class InvalidCapacityException : Exception
{
}
}
6 changes: 6 additions & 0 deletions ParkingLotApi/Exceptions/PageInfoInvalidException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ParkingLotApi.Exceptions
{
public class PageInfoInvalidException : Exception
{
}
}
6 changes: 6 additions & 0 deletions ParkingLotApi/Exceptions/ParkingLotNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ParkingLotApi.Exceptions

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well: nice to define customized exception

{
public class ParkingLotNotFoundException : Exception
{
}
}
25 changes: 25 additions & 0 deletions ParkingLotApi/Filters/InvalidCapacityExceptionFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using ParkingLotApi.Exceptions;

namespace ParkingLotApi.Filters
{
public class InvalidCapacityExceptionFilter : IActionFilter, IOrderedFilter
{
int IOrderedFilter.Order => int.MaxValue - 10;

void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception is InvalidCapacityException invalidCapacityException)
{
context.Result = new BadRequestResult();
context.ExceptionHandled = true;
}
}

void IActionFilter.OnActionExecuting(ActionExecutingContext context)
{
}
}
}
24 changes: 24 additions & 0 deletions ParkingLotApi/Filters/PageInfoInvalidExceptionFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using ParkingLotApi.Exceptions;

namespace ParkingLotApi.Filters
{
public class PageInfoInvalidExceptionFilter : IActionFilter, IOrderedFilter

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well: nice to add more than one exception filter

{
int IOrderedFilter.Order => int.MaxValue - 10;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: this value might need to be different for each filter


void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception is PageInfoInvalidException pageInfoInvalidException)
{
context.Result = new BadRequestObjectResult(context.Exception.Message);
context.ExceptionHandled = true;
}
}

void IActionFilter.OnActionExecuting(ActionExecutingContext context)
{
}
}
}
24 changes: 24 additions & 0 deletions ParkingLotApi/Filters/ParkingLotNotFoundExceptionFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using ParkingLotApi.Exceptions;

namespace ParkingLotApi.Filters
{
public class ParkingLotNotFoundExceptionFilter :IActionFilter, IOrderedFilter
{
int IOrderedFilter.Order => int.MaxValue - 10;

void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception is ParkingLotNotFoundException parkingLotNotFoundException)
{
context.Result = new NotFoundObjectResult(context.Exception.Message);
context.ExceptionHandled = true;
}
}

void IActionFilter.OnActionExecuting(ActionExecutingContext context)
{
}
}
}
15 changes: 15 additions & 0 deletions ParkingLotApi/Models/ParkingLot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace ParkingLotApi.Models
{
public class ParkingLot
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }
public string Name { get; set; }
public int Capacity { get; set; }
public string Location { get; set; }
}
}
9 changes: 9 additions & 0 deletions ParkingLotApi/Models/ParkingLotDatabaseSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace ParkingLotApi.Models
{
public class ParkingLotDatabaseSettings
{
public string ConnectionString { get; set; } = null!;
public string DatabaseName { get; set; } = null!;
public string CollectionName { get; set; } = null!;
}
}
2 changes: 2 additions & 0 deletions ParkingLotApi/ParkingLotApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.13" />
<PackageReference Include="MongoDB.Bson" Version="2.22.0" />
<PackageReference Include="MongoDB.Driver" Version="2.22.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

Expand Down
18 changes: 16 additions & 2 deletions ParkingLotApi/Program.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
using ParkingLotApi.Filters;
using ParkingLotApi.Models;
using ParkingLotApi.Repositories;
using ParkingLotApi.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddControllers(options =>
{
options.Filters.Add<InvalidCapacityExceptionFilter>();
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped<ParkingLotService>();
builder.Services.AddSingleton<IParkingLotsRepository, ParkingLotsRepository>();
builder.Services.Configure<ParkingLotDatabaseSettings>(
builder.Configuration.GetSection("ParkingLotStoreDatabase"));

var app = builder.Build();

Expand All @@ -22,4 +34,6 @@

app.MapControllers();

app.Run();
app.Run();

public partial class Program { }
14 changes: 14 additions & 0 deletions ParkingLotApi/Repositories/IParkingLotsRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using ParkingLotApi.Models;

namespace ParkingLotApi.Repositories
{
public interface IParkingLotsRepository
{
public Task<ParkingLot> CreateParkingLot(ParkingLot parking);
public Task<ParkingLot> GetParkingLotById(string id);
public Task DeleteParkingLotById(string id);
public Task<List<ParkingLot>> GetParkingLotByPageInfo(int pageIndex, int pageSize);
public Task<List<ParkingLot>> GetAllParkingLot();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: there is no requirement for getting all data

public Task<ParkingLot> UpdateParkingLotInfo(string id, ParkingLot parkingLot);
}
}
51 changes: 51 additions & 0 deletions ParkingLotApi/Repositories/ParkingLotsRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using ParkingLotApi.Models;

namespace ParkingLotApi.Repositories
{
public class ParkingLotsRepository : IParkingLotsRepository
{
private readonly IMongoCollection<ParkingLot> parkingLotCollection;

public ParkingLotsRepository(IOptions<ParkingLotDatabaseSettings> parkingLotDatabaseSettings)
{
var mongoClient = new MongoClient(parkingLotDatabaseSettings.Value.ConnectionString);
var mongoDatabase = mongoClient.GetDatabase(parkingLotDatabaseSettings.Value.DatabaseName);
parkingLotCollection = mongoDatabase.GetCollection<ParkingLot>(parkingLotDatabaseSettings.Value.CollectionName);
}

public async Task<ParkingLot> CreateParkingLot(ParkingLot parkingLot)
{
await parkingLotCollection.InsertOneAsync(parkingLot);
return await parkingLotCollection.Find(a => a.Name == parkingLot.Name).FirstAsync();
}

public async Task<ParkingLot> GetParkingLotById(string id)
{
return await parkingLotCollection.Find(parkingLot => parkingLot.Id == id).FirstOrDefaultAsync();
}

public async Task DeleteParkingLotById(string id)
{
await parkingLotCollection.DeleteOneAsync(parkingLot => parkingLot.Id == id);
}

public async Task<List<ParkingLot>> GetParkingLotByPageInfo(int pageIndex, int pageSize)
{
return await parkingLotCollection.Find(parkingLot => true)
.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
}

public async Task<List<ParkingLot>> GetAllParkingLot()
{
return await parkingLotCollection.Find(parkingLot => true).ToListAsync();
}

public async Task<ParkingLot> UpdateParkingLotInfo(string id, ParkingLot parkingLot)
{
await parkingLotCollection.ReplaceOneAsync(parkingLot => parkingLot.Id == id, parkingLot);
return parkingLot;
}
}
}
82 changes: 82 additions & 0 deletions ParkingLotApi/Services/ParkingLotService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using ParkingLotApi.Dtos;
using ParkingLotApi.Exceptions;
using ParkingLotApi.Models;
using ParkingLotApi.Repositories;

namespace ParkingLotApi.Services
{
public class ParkingLotService
{
private readonly IParkingLotsRepository parkingLotsRepository;

public ParkingLotService(IParkingLotsRepository parkingLotsRepository)
{
this.parkingLotsRepository = parkingLotsRepository;
}

public async Task<ParkingLot> CreateAsync(ParkingLotDto parkingLotDto)
{
if (parkingLotDto.Capacity < 10)
{
throw new InvalidCapacityException();
}

return await parkingLotsRepository.CreateParkingLot(parkingLotDto.ToEntity());
}

public async Task DeleteParkingLot(string id)
{
if (await parkingLotsRepository.GetParkingLotById(id) == null)
{
throw new ParkingLotNotFoundException();
}
await parkingLotsRepository.DeleteParkingLotById(id);
}

public async Task<List<ParkingLot>> GetParkingLotByPageInfo(int pageIndex)
{
int pageSize = 15;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: this variable can be extracted as constant


if (pageIndex < 0)
{
throw new PageInfoInvalidException();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good:throw exception in service

}

if (pageIndex == 0)
{
return await parkingLotsRepository.GetAllParkingLot();
}
else
{
return await parkingLotsRepository.GetParkingLotByPageInfo(pageIndex, pageSize);
}
}

public async Task<ParkingLot> GetParkingLotById(string id)
{
ParkingLot parkingLot = await parkingLotsRepository.GetParkingLotById(id);

if (parkingLot == null)
{
throw new ParkingLotNotFoundException();
}

return parkingLot;
}

public async Task<ParkingLot> UpdateParkingLot(string id, ParkingLotDto parkingLotDto)
{
ParkingLot parkingLot = await parkingLotsRepository.GetParkingLotById(id);

if (parkingLot == null)
{
throw new ParkingLotNotFoundException();
}

parkingLot.Location = parkingLotDto.Location;
parkingLot.Capacity = (int)parkingLotDto.Capacity;

return await parkingLotsRepository.UpdateParkingLotInfo(id, parkingLot);
}
}
}
Loading