Skip to content

Commit dd89288

Browse files
Merge pull request #656 from PathfinderHonorManager/develop
Added club POST and PUT
2 parents 55b9b7d + c138f9e commit dd89288

File tree

6 files changed

+134
-16
lines changed

6 files changed

+134
-16
lines changed

PathfinderHonorManager.Tests/PathfinderHonorManager.Tests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
<PackageReference Include="NUnit" Version="4.3.2" />
1818
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
1919
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
20-
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.3" />
21-
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.3" />
20+
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.4" />
21+
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
2222
<PackageReference Include="FluentValidation.Validators.UnitTestExtension" Version="1.11.0.2" />
2323
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.11.0" />
24-
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.3" />
24+
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.4" />
2525
</ItemGroup>
2626

2727
<ItemGroup>

PathfinderHonorManager/Controllers/ClubController.cs

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
using PathfinderHonorManager.Service.Interfaces;
1010
using Microsoft.Extensions.Logging;
1111
using System.Linq;
12+
using PathfinderHonorManager.Dto.Incoming;
13+
using FluentValidation;
14+
using Microsoft.EntityFrameworkCore;
1215

1316
namespace PathfinderHonorManager.Controllers
1417
{
@@ -19,15 +22,20 @@ namespace PathfinderHonorManager.Controllers
1922
[Consumes("application/json")]
2023
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
2124
[ProducesResponseType(StatusCodes.Status403Forbidden)]
22-
public class ClubsController : ControllerBase
25+
public class ClubsController : CustomApiController
2326
{
2427
private readonly IClubService _clubService;
2528
private readonly ILogger<ClubsController> _logger;
29+
private readonly IValidator<ClubDto> _validator;
2630

27-
public ClubsController(IClubService clubService, ILogger<ClubsController> logger)
31+
public ClubsController(
32+
IClubService clubService,
33+
ILogger<ClubsController> logger,
34+
IValidator<ClubDto> validator)
2835
{
2936
_clubService = clubService;
3037
_logger = logger;
38+
_validator = validator;
3139
}
3240

3341
// GET Clubs
@@ -80,7 +88,7 @@ public async Task<ActionResult<IEnumerable<Club>>> GetClubs(CancellationToken to
8088
/// <returns></returns>
8189
[ProducesResponseType(StatusCodes.Status200OK)]
8290
[ProducesResponseType(StatusCodes.Status404NotFound)]
83-
[HttpGet("{id:guid}")]
91+
[HttpGet("{id:guid}", Name = "GetClubById")]
8492
public async Task<IActionResult> GetByIdAsync(Guid id, CancellationToken token)
8593
{
8694
_logger.LogInformation("Getting club with ID {ClubId}", id);
@@ -95,5 +103,54 @@ public async Task<IActionResult> GetByIdAsync(Guid id, CancellationToken token)
95103
_logger.LogInformation("Retrieved club with ID {ClubId}", id);
96104
return Ok(club);
97105
}
106+
107+
[Authorize("CreateClubs")]
108+
[ProducesResponseType(StatusCodes.Status201Created)]
109+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
110+
[HttpPost]
111+
public async Task<IActionResult> CreateAsync([FromBody] ClubDto club, CancellationToken token)
112+
{
113+
_logger.LogInformation("Creating new club with code {ClubCode}", club.ClubCode);
114+
115+
try
116+
{
117+
var createdClub = await _clubService.CreateAsync(club, token);
118+
return CreatedAtRoute("GetClubById", new { id = createdClub.ClubID }, createdClub);
119+
}
120+
catch (FluentValidation.ValidationException ex)
121+
{
122+
_logger.LogWarning(ex, "Validation failed while creating club with code {ClubCode}", club.ClubCode);
123+
UpdateModelState(ex);
124+
return ValidationProblem(ModelState);
125+
}
126+
}
127+
128+
[Authorize("UpdateClubs")]
129+
[ProducesResponseType(StatusCodes.Status200OK)]
130+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
131+
[ProducesResponseType(StatusCodes.Status404NotFound)]
132+
[HttpPut("{id:guid}")]
133+
public async Task<IActionResult> UpdateAsync(Guid id, [FromBody] ClubDto club, CancellationToken token)
134+
{
135+
_logger.LogInformation("Updating club with ID {ClubId}", id);
136+
137+
try
138+
{
139+
var updatedClub = await _clubService.UpdateAsync(id, club, token);
140+
if (updatedClub == default)
141+
{
142+
_logger.LogWarning("Club with ID {ClubId} not found for update", id);
143+
return NotFound();
144+
}
145+
146+
return Ok(updatedClub);
147+
}
148+
catch (FluentValidation.ValidationException ex)
149+
{
150+
_logger.LogWarning(ex, "Validation failed while updating club with ID {ClubId}", id);
151+
UpdateModelState(ex);
152+
return ValidationProblem(ModelState);
153+
}
154+
}
98155
}
99156
}

PathfinderHonorManager/Mapping/AutoMapperConfig.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ private void RegisterHonorMappings()
5555
private void RegisterClubMappings()
5656
{
5757
CreateMap<Club, Outgoing.ClubDto>();
58+
CreateMap<Incoming.ClubDto, Club>();
5859
}
5960

6061
private void RegisterPathfinderHonorMappings()

PathfinderHonorManager/PathfinderHonorManager.csproj

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@
1111
<ItemGroup>
1212
<PackageReference Include="FluentValidation" Version="11.11.0" />
1313
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.11.0" />
14-
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
15-
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.3" />
16-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" />
14+
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
15+
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.4" />
16+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
1717
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
1818
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />
1919
<PackageReference Include="Npgsql" Version="9.0.3" />
20-
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.3" />
20+
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.4" />
2121
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
2222
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
23-
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.0" />
24-
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.3" />
23+
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
24+
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.4" />
2525
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
2626
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="9.0.2" />
27-
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="9.0.3" />
28-
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.3" />
29-
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="9.0.3" />
30-
<PackageReference Include="Microsoft.Identity.Web" Version="3.6.2" />
27+
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="9.0.4" />
28+
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
29+
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="9.0.4" />
30+
<PackageReference Include="Microsoft.Identity.Web" Version="3.8.4" />
3131
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.23.0" />
3232
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
3333
</ItemGroup>

PathfinderHonorManager/Service/ClubService.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
using System.Threading;
66
using System.Threading.Tasks;
77
using Outgoing = PathfinderHonorManager.Dto.Outgoing;
8+
using Incoming = PathfinderHonorManager.Dto.Incoming;
89
using PathfinderHonorManager.Model;
910
using PathfinderHonorManager.DataAccess;
1011
using PathfinderHonorManager.Service.Interfaces;
1112
using System.Collections.Generic;
1213
using System.Linq;
14+
using FluentValidation;
1315

1416
namespace PathfinderHonorManager.Service
1517
{
@@ -79,5 +81,53 @@ public ClubService(
7981
_logger.LogInformation("Retrieved club: {ClubName} (Code: {ClubCode})", entity.Name, code);
8082
return _mapper.Map<Outgoing.ClubDto>(entity);
8183
}
84+
85+
public async Task<Outgoing.ClubDto> CreateAsync(Incoming.ClubDto club, CancellationToken token)
86+
{
87+
_logger.LogInformation("Creating new club with code: {ClubCode}", club.ClubCode);
88+
89+
try
90+
{
91+
var entity = _mapper.Map<Club>(club);
92+
await _dbContext.Clubs.AddAsync(entity, token);
93+
await _dbContext.SaveChangesAsync(token);
94+
95+
_logger.LogInformation("Created club: {ClubName} (ID: {ClubId})", entity.Name, entity.ClubID);
96+
return _mapper.Map<Outgoing.ClubDto>(entity);
97+
}
98+
catch (DbUpdateException ex)
99+
{
100+
_logger.LogError(ex, "Database error while creating club with code {ClubCode}", club.ClubCode);
101+
throw new ValidationException("Failed to create club. The club code may already be in use.");
102+
}
103+
}
104+
105+
public async Task<Outgoing.ClubDto> UpdateAsync(Guid id, Incoming.ClubDto club, CancellationToken token)
106+
{
107+
_logger.LogInformation("Updating club with ID: {ClubId}", id);
108+
109+
try
110+
{
111+
var entity = await _dbContext.Clubs
112+
.SingleOrDefaultAsync(c => c.ClubID == id, token);
113+
114+
if (entity == default)
115+
{
116+
_logger.LogWarning("Club with ID {ClubId} not found", id);
117+
return default;
118+
}
119+
120+
_mapper.Map(club, entity);
121+
await _dbContext.SaveChangesAsync(token);
122+
123+
_logger.LogInformation("Updated club: {ClubName} (ID: {ClubId})", entity.Name, id);
124+
return _mapper.Map<Outgoing.ClubDto>(entity);
125+
}
126+
catch (DbUpdateException ex)
127+
{
128+
_logger.LogError(ex, "Database error while updating club with ID {ClubId}", id);
129+
throw new ValidationException("Failed to update club. The club code may already be in use.");
130+
}
131+
}
82132
}
83133
}

PathfinderHonorManager/Service/Interfaces/IClubService.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Threading;
44
using System.Threading.Tasks;
55
using Outgoing = PathfinderHonorManager.Dto.Outgoing;
6+
using Incoming = PathfinderHonorManager.Dto.Incoming;
67

78
namespace PathfinderHonorManager.Service.Interfaces
89
{
@@ -18,5 +19,14 @@ public interface IClubService
1819

1920
Task<ICollection<Outgoing.ClubDto>> GetAllAsync(
2021
CancellationToken token);
22+
23+
Task<Outgoing.ClubDto> CreateAsync(
24+
Incoming.ClubDto club,
25+
CancellationToken token);
26+
27+
Task<Outgoing.ClubDto> UpdateAsync(
28+
Guid id,
29+
Incoming.ClubDto club,
30+
CancellationToken token);
2131
}
2232
}

0 commit comments

Comments
 (0)