Skip to content

Commit 4751d29

Browse files
committed
Versioned V1 timesheet API contracts and services
Introduce V1 namespace for timesheet HTTP contracts, validators, and mappers. Rename DTOs and services (e.g., AddTimesheetHttpRequest → AddTimesheetHttpRequestV1, TimesheetApplication → TimesheetApplicationV1). Update all usages in controllers, tests, and infrastructure to use V1 types and interfaces, enabling clean API versioning and future extensibility.
1 parent 86efb2c commit 4751d29

28 files changed

Lines changed: 138 additions & 128 deletions

src/Azure.Local.ApiService/Azure.Local.ApiService.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
<None Remove="Health\Contracts\**" />
1515
</ItemGroup>
1616

17+
<ItemGroup>
18+
<None Include="Timesheets\Contracts\V1\ChangeTimesheetStateHttpRequestV1.cs" />
19+
</ItemGroup>
20+
1721
<ItemGroup>
1822
<ProjectReference Include="..\application\Azure.Local.Application\Azure.Local.Application.csproj" />
1923
<ProjectReference Include="..\Azure.Local.ServiceDefaults\Azure.Local.ServiceDefaults.csproj" />

src/Azure.Local.ApiService/Program.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using Azure.Local.ApiService.Timesheets.Contracts;
2-
using Azure.Local.ApiService.Timesheets.Controllers.Validators;
1+
using Azure.Local.ApiService.Timesheets.Contracts.V1;
2+
using Azure.Local.ApiService.Timesheets.Controllers.Validators.V1;
33
using Azure.Local.ApiService.Timesheets.Mapping.V1;
44
using Azure.Local.ApiService.Versioning;
55
using Azure.Local.Application;
@@ -30,9 +30,9 @@ static async Task Main(string[] args)
3030
.AddInfrastructure(builder.Configuration);
3131

3232
// Register validators
33-
builder.Services.AddScoped<IValidator<AddTimesheetHttpRequest>, AddTimesheetHttpRequestValidator>();
34-
builder.Services.AddScoped<IValidator<PatchTimesheetHttpRequest>, PatchTimesheetHttpRequestValidator>();
35-
builder.Services.AddScoped<IValidator<ChangeTimesheetStateHttpRequest>, ChangeTimesheetStateHttpRequestValidator>();
33+
builder.Services.AddScoped<IValidator<AddTimesheetHttpRequestV1>, AddTimesheetHttpRequestValidatorV1>();
34+
builder.Services.AddScoped<IValidator<PatchTimesheetHttpRequestV1>, PatchTimesheetHttpRequestValidatorV1>();
35+
builder.Services.AddScoped<IValidator<ChangeTimesheetStateHttpRequestV1>, ChangeTimesheetStateHttpRequestValidatorV1>();
3636
builder.Services.AddSingleton<ITimesheetContractMapperV1, TimesheetContractMapperV1>();
3737

3838
// Add services to the container.

src/Azure.Local.ApiService/Timesheets/Contracts/PatchTimesheetHttpRequest.cs

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/Azure.Local.ApiService/Timesheets/Contracts/AddTimesheetHttpRequest.cs renamed to src/Azure.Local.ApiService/Timesheets/Contracts/V1/AddTimesheetHttpRequestV1.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using System.ComponentModel.DataAnnotations;
22

3-
namespace Azure.Local.ApiService.Timesheets.Contracts
3+
namespace Azure.Local.ApiService.Timesheets.Contracts.V1
44
{
5-
public class AddTimesheetHttpRequest
5+
public class AddTimesheetHttpRequestV1
66
{
77
public string Id { get; set; } = Guid.NewGuid().ToString();
88

@@ -15,7 +15,7 @@ public class AddTimesheetHttpRequest
1515
[Required]
1616
public required DateTime To { get; set; }
1717

18-
public List<TimesheetHttpRequestComponent> Components { get; set; } = [];
18+
public List<TimesheetHttpRequestComponentV1> Components { get; set; } = [];
1919

2020
// Metadata
2121
[MaxLength(50)]

src/Azure.Local.ApiService/Timesheets/Contracts/ChangeTimesheetStateHttpRequest.cs renamed to src/Azure.Local.ApiService/Timesheets/Contracts/V1/ChangeTimesheetStateHttpRequestV1.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using Azure.Local.Domain.Timesheets;
22

3-
namespace Azure.Local.ApiService.Timesheets.Contracts
3+
namespace Azure.Local.ApiService.Timesheets.Contracts.V1
44
{
55
/// <summary>
66
/// Request to change the state of a timesheet
77
/// </summary>
8-
public class ChangeTimesheetStateHttpRequest
8+
public class ChangeTimesheetStateHttpRequestV1
99
{
1010
/// <summary>
1111
/// The target state action to perform
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Azure.Local.ApiService.Timesheets.Contracts.V1
2+
{
3+
public class PatchTimesheetHttpRequestV1 : AddTimesheetHttpRequestV1
4+
{
5+
// Concurrency control
6+
public string? ETag { get; set; }
7+
}
8+
}

src/Azure.Local.ApiService/Timesheets/Contracts/TimesheetHttpRequestComponent.cs renamed to src/Azure.Local.ApiService/Timesheets/Contracts/V1/TimesheetHttpRequestComponentV1.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using System.ComponentModel.DataAnnotations;
22

3-
namespace Azure.Local.ApiService.Timesheets.Contracts
3+
namespace Azure.Local.ApiService.Timesheets.Contracts.V1
44
{
5-
public class TimesheetHttpRequestComponent
5+
public class TimesheetHttpRequestComponentV1
66
{
77
// Identity (optional for new, required for updates)
88
public string? Id { get; set; }

src/Azure.Local.ApiService/Timesheets/Contracts/TimesheetResponseComponent.cs renamed to src/Azure.Local.ApiService/Timesheets/Contracts/V1/TimesheetResponseComponentV1.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
namespace Azure.Local.ApiService.Timesheets.Contracts
1+
namespace Azure.Local.ApiService.Timesheets.Contracts.V1
22
{
33
/// <summary>
44
/// Component (line item) in a timesheet response
55
/// </summary>
6-
public class TimesheetResponseComponent
6+
public class TimesheetResponseComponentV1
77
{
88
// Identity
99
public required string Id { get; set; }

src/Azure.Local.ApiService/Timesheets/Contracts/TimesheetResponse.cs renamed to src/Azure.Local.ApiService/Timesheets/Contracts/V1/TimesheetResponseV1.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
namespace Azure.Local.ApiService.Timesheets.Contracts
1+
namespace Azure.Local.ApiService.Timesheets.Contracts.V1
22
{
33
/// <summary>
44
/// Timesheet response with full details for GET operations
55
/// </summary>
6-
public class TimesheetResponse
6+
public class TimesheetResponseV1
77
{
88
// Core
99
public required string Id { get; set; }
1010
public required string PersonId { get; set; }
1111
public required DateTime From { get; set; }
1212
public required DateTime To { get; set; }
13-
public List<TimesheetResponseComponent> Components { get; set; } = [];
13+
public List<TimesheetResponseComponentV1> Components { get; set; } = [];
1414

1515
// Workflow
1616
public required string Status { get; set; }

src/Azure.Local.ApiService/Timesheets/Controllers/TimesheetControllerV1.cs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,30 @@
1-
using Azure.Local.ApiService.Timesheets.Contracts;
1+
using Azure.Local.ApiService.Timesheets.Contracts.V1;
22
using Azure.Local.ApiService.Timesheets.Mapping.V1;
33
using Azure.Local.ApiService.Versioning;
44
using Azure.Local.Application.Timesheets;
5+
using Azure.Local.Application.Timesheets.V1;
56
using FluentValidation;
67

7-
namespace Azure.Local.ApiService.Timesheets.Controllers.V1
8+
namespace Azure.Local.ApiService.Timesheets.Controllers
89
{
910
[ApiController]
1011
[ApiVersion(ApiVersioningConstants.V1)]
1112
[Route("person")]
1213
public class TimesheetControllerV1(
1314
ITimesheetApplicationV1 timesheetApplication,
1415
ITimesheetContractMapperV1 mapper,
15-
IValidator<AddTimesheetHttpRequest> addTestItemHttpRequestValidator,
16-
IValidator<PatchTimesheetHttpRequest> patchTestItemHttpRequestValidator,
17-
IValidator<ChangeTimesheetStateHttpRequest> changeTimesheetStateHttpRequestValidator) : ControllerBase
16+
IValidator<AddTimesheetHttpRequestV1> addTestItemHttpRequestValidator,
17+
IValidator<PatchTimesheetHttpRequestV1> patchTestItemHttpRequestValidator,
18+
IValidator<ChangeTimesheetStateHttpRequestV1> changeTimesheetStateHttpRequestValidator) : ControllerBase
1819
{
1920
private readonly ITimesheetApplicationV1 _timesheetApplication = timesheetApplication;
2021
private readonly ITimesheetContractMapperV1 _mapper = mapper;
21-
private readonly IValidator<AddTimesheetHttpRequest> _addTestItemHttpRequestValidator = addTestItemHttpRequestValidator;
22-
private readonly IValidator<PatchTimesheetHttpRequest> _patchTestItemHttpRequestValidator = patchTestItemHttpRequestValidator;
23-
private readonly IValidator<ChangeTimesheetStateHttpRequest> _changeTimesheetStateHttpRequestValidator = changeTimesheetStateHttpRequestValidator;
22+
private readonly IValidator<AddTimesheetHttpRequestV1> _addTestItemHttpRequestValidator = addTestItemHttpRequestValidator;
23+
private readonly IValidator<PatchTimesheetHttpRequestV1> _patchTestItemHttpRequestValidator = patchTestItemHttpRequestValidator;
24+
private readonly IValidator<ChangeTimesheetStateHttpRequestV1> _changeTimesheetStateHttpRequestValidator = changeTimesheetStateHttpRequestValidator;
2425

2526
[HttpPost("{personId}/timesheet/item")]
26-
public async Task<IActionResult> Post([FromRoute] string personId, AddTimesheetHttpRequest request)
27+
public async Task<IActionResult> Post([FromRoute] string personId, AddTimesheetHttpRequestV1 request)
2728
{
2829
if (request.PersonId != personId)
2930
return BadRequest("PersonId in URL does not match PersonId in request body.");
@@ -53,7 +54,7 @@ public async Task<IActionResult> Post([FromRoute] string personId, AddTimesheetH
5354
}
5455

5556
[HttpPatch("{personId}/timesheet/item")]
56-
public async Task<IActionResult> Patch([FromRoute] string personId, PatchTimesheetHttpRequest request)
57+
public async Task<IActionResult> Patch([FromRoute] string personId, PatchTimesheetHttpRequestV1 request)
5758
{
5859
if (request.PersonId != personId)
5960
return BadRequest("PersonId in URL does not match PersonId in request body.");
@@ -135,7 +136,7 @@ public async Task<IActionResult> Delete(string personId, string id)
135136
}
136137

137138
[HttpPost("{personId}/timesheet/item/{id}/state")]
138-
public async Task<IActionResult> ChangeState([FromRoute] string personId, [FromRoute] string id, ChangeTimesheetStateHttpRequest request)
139+
public async Task<IActionResult> ChangeState([FromRoute] string personId, [FromRoute] string id, ChangeTimesheetStateHttpRequestV1 request)
139140
{
140141
var validationResult = _changeTimesheetStateHttpRequestValidator.Validate(request);
141142
if (!validationResult.IsValid)
@@ -148,7 +149,7 @@ public async Task<IActionResult> ChangeState([FromRoute] string personId, [FromR
148149
}
149150
catch (InvalidOperationException ex)
150151
{
151-
return BadRequest(new { Message = ex.Message });
152+
return BadRequest(new { ex.Message });
152153
}
153154
catch (Exception ex)
154155
{
@@ -169,7 +170,7 @@ public async Task<IActionResult> Reopen([FromRoute] string personId, [FromRoute]
169170
}
170171
catch (InvalidOperationException ex)
171172
{
172-
return BadRequest(new { Message = ex.Message });
173+
return BadRequest(new { ex.Message });
173174
}
174175
catch (Exception ex)
175176
{

0 commit comments

Comments
 (0)