Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Migrations/20240625_AddShiftRatings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
public partial class AddShiftRatings : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ShiftRatings",
columns: table => new
{
ShiftId = table.Column<int>(nullable: false),
FacilityId = table.Column<int>(nullable: false),
WorkerId = table.Column<int>(nullable: false),
Score = table.Column<int>(nullable: false)
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

there's a missing property to add an optional comment (nullable).

constraints: table =>
{
table.PrimaryKey("PK_ShiftRatings", x => new { x.ShiftId, x.WorkerId });
table.ForeignKey("FK_ShiftRatings_Shifts_ShiftId", x => x.ShiftId, "Shifts", "Id", onDelete: ReferentialAction.Cascade);
table.ForeignKey("FK_ShiftRatings_Facilities_FacilityId", x => x.FacilityId, "Facilities", "Id", onDelete: ReferentialAction.Cascade);
table.ForeignKey("FK_ShiftRatings_Workers_WorkerId", x => x.WorkerId, "Workers", "Id", onDelete: ReferentialAction.Cascade);
});

migrationBuilder.CreateIndex("IX_ShiftRatings_FacilityId", "ShiftRatings", "FacilityId");
migrationBuilder.CreateIndex("IX_ShiftRatings_WorkerId", "ShiftRatings", "WorkerId");
}

protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable("ShiftRatings");
}
}
29 changes: 26 additions & 3 deletions src/Controllers/FacilitiesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,31 @@
public class FacilitiesController : ControllerBase
{
private readonly AppDbContext _dbContext;
public FacilitiesController(AppDbContext dbContext) => _dbContext = dbContext;
private readonly RateWorkerHandler _rateWorkerHandler;
private readonly BlockWorkerHandler _blockWorkerHandler;

[HttpGet]
public IActionResult GetFacilities() => Ok(_dbContext.Facilities.ToList());
public FacilitiesController(AppDbContext dbContext, RateWorkerHandler rateWorkerHandler, BlockWorkerHandler blockWorkerHandler)
Copy link
Copy Markdown

@plussenhoff plussenhoff Mar 16, 2026

Choose a reason for hiding this comment

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

we should not expose a dbContext here. It should live within a Repository class.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this class is not following dependency inversion principle. We should use abstractions (interfaces) instead of implementations. Delegate instance creation to the framework by registering dependencies in the DI container to the proper lifetime.

{
_dbContext = dbContext;
_rateWorkerHandler = rateWorkerHandler;
_blockWorkerHandler = blockWorkerHandler;
}

[HttpGet("facilities/list")]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

we should use relative paths, otherwise we could end up having duplicate segments.

public async Task<IActionResult> GetFacilities([FromBody] RateWorkerRequest request)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

GET methods should not use Body according to conventions.

{
return await _dbContext.Facilities.ToListAsync();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

dbContext should not be exposed here. There should be a handler and a repository containing the dbContext and queries. We should not return full domain entities, use DTOs instead and return a proper status code (Ok, BadRequest, NotFound, etc).

}

[HttpPost("facilities/rate-worker")]
public async Task<IActionResult> RateWorker([FromBody] RateWorkerRequest request)
{
return await _rateWorkerHandler.HandleAsync(request);
}

[HttpPost("facilities/block-worker")]
public async Task<IActionResult> BlockWorker([FromBody] BlockWorkerRequest request)
{
return await _blockWorkerHandler.HandleAsync(request);
}
}
21 changes: 21 additions & 0 deletions src/Handlers/BlockWorkerHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
public class BlockWorkerHandler
{
private readonly WorkerRepository _workerRepository;

public BlockWorkerHandler(WorkerRepository workerRepository)
{
_workerRepository = workerRepository;
}

public async Task<IActionResult> HandleAsync(BlockWorkerRequest request)
{
try
{
_workerRepository.BlockWorker(request.WorkerId, request.FacilityId);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

calling a blocking operation like requests to DB, files, other API synchronously will block the current thread and can lead to thread pool starvation. Add await for this kind of operations as a good practice to release the thread so it can.

}
catch (Exception e)
{
return new JsonResult(new { error = e.Message });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

there should be a log for this exception and we could return a general message to the client.

}
}
}
32 changes: 32 additions & 0 deletions src/Handlers/RateWorkerHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
public class RateWorkerHandler
{
private readonly AppDbContext _dbContext;

public RateWorkerHandler(AppDbContext dbContext)
{
_dbContext = dbContext;
}

public async Task<IActionResult> HandleAsync(RateWorkerRequest request)
{
try
{
var shiftRating = new ShiftRating
{
ShiftId = request.ShiftId,
FacilityId = request.FacilityId,
WorkerId = request.WorkerId,
Score = request.Score
};

_dbContext.ShiftRatings.Add(shiftRating);
await _dbContext.SaveChangesAsync();

return new JsonResult(shiftRating);
}
catch (Exception e)
{
return new JsonResult(new { error = e.Message });
}
}
}
13 changes: 13 additions & 0 deletions src/Models/ShiftRating.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
public class ShiftRating
{
public int ShiftId { get; set; }
public Shift Shift { get; set; }

public int FacilityId { get; set; }
public Facility Facility { get; set; }

public int WorkerId { get; set; }
public Worker Worker { get; set; }

public int Score { get; set; }
}
1 change: 1 addition & 0 deletions src/Models/Worker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ public class Worker
{
public int Id { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public List<Shift> Shifts { get; set; }
}