-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: TaskOwnerReport code without KPIs and tests
- Loading branch information
1 parent
74772ba
commit 18e54a9
Showing
19 changed files
with
764 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
src/Fusion.Summary.Api/Controllers/ApiModels/ApiProject.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using Fusion.Summary.Api.Domain.Models; | ||
|
||
namespace Fusion.Summary.Api.Controllers.ApiModels; | ||
|
||
public class ApiProject | ||
{ | ||
public required Guid Id { get; set; } | ||
|
||
public required string Name { get; set; } | ||
public required Guid OrgProjectExternalId { get; set; } | ||
|
||
public Guid? DirectorAzureUniqueId { get; set; } | ||
|
||
public Guid[] AssignedAdminsAzureUniqueId { get; set; } = []; | ||
|
||
|
||
public static ApiProject FromQueryProject(QueryProject queryProject) | ||
{ | ||
return new ApiProject() | ||
{ | ||
Id = queryProject.Id, | ||
Name = queryProject.Name, | ||
OrgProjectExternalId = queryProject.OrgProjectExternalId, | ||
AssignedAdminsAzureUniqueId = queryProject.AssignedAdminsAzureUniqueId.ToArray(), | ||
DirectorAzureUniqueId = queryProject.DirectorAzureUniqueId | ||
}; | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/Fusion.Summary.Api/Controllers/ApiModels/ApiWeeklyTaskOwnerReport.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using Fusion.Summary.Api.Domain.Models; | ||
|
||
namespace Fusion.Summary.Api.Controllers.ApiModels; | ||
|
||
public class ApiWeeklyTaskOwnerReport | ||
{ | ||
public required Guid Id { get; set; } | ||
public required Guid ProjectId { get; set; } | ||
public required DateTime PeriodStart { get; set; } | ||
public required DateTime PeriodEnd { get; set; } | ||
|
||
|
||
public static ApiWeeklyTaskOwnerReport FromQueryWeeklyTaskOwnerReport(QueryWeeklyTaskOwnerReport queryWeeklyTaskOwnerReport) | ||
{ | ||
return new ApiWeeklyTaskOwnerReport | ||
{ | ||
Id = queryWeeklyTaskOwnerReport.Id, | ||
ProjectId = queryWeeklyTaskOwnerReport.ProjectId, | ||
PeriodStart = queryWeeklyTaskOwnerReport.Period.Start, | ||
PeriodEnd = queryWeeklyTaskOwnerReport.Period.End | ||
}; | ||
} | ||
} |
121 changes: 121 additions & 0 deletions
121
src/Fusion.Summary.Api/Controllers/ProjectsController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
using System.Net.Mime; | ||
using Fusion.AspNetCore.FluentAuthorization; | ||
using Fusion.Authorization; | ||
using Fusion.Integration.Profile; | ||
using Fusion.Summary.Api.Authorization.Extensions; | ||
using Fusion.Summary.Api.Controllers.ApiModels; | ||
using Fusion.Summary.Api.Controllers.Requests; | ||
using Fusion.Summary.Api.Domain.Commands; | ||
using Fusion.Summary.Api.Domain.Queries; | ||
using Microsoft.ApplicationInsights.AspNetCore.Extensions; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
namespace Fusion.Summary.Api.Controllers; | ||
|
||
[Authorize] | ||
[ApiController] | ||
[Produces(MediaTypeNames.Application.Json)] | ||
[ApiVersion("1.0")] | ||
public class ProjectsController : BaseController | ||
{ | ||
[HttpGet("projects")] | ||
[MapToApiVersion("1.0")] | ||
[ProducesResponseType(StatusCodes.Status200OK)] | ||
public async Task<ActionResult<ApiProject[]>> GetProjectsV1() | ||
{ | ||
#region Authorization | ||
|
||
var authResult = await Request.RequireAuthorizationAsync(r => | ||
{ | ||
r.AlwaysAccessWhen().ResourcesFullControl(); | ||
r.AnyOf(or => { or.BeTrustedApplication(); }); | ||
}); | ||
|
||
if (authResult.Unauthorized) | ||
return authResult.CreateForbiddenResponse(); | ||
|
||
#endregion Authorization | ||
|
||
var projects = await DispatchAsync(new GetProjects()); | ||
|
||
var apiProjects = projects.Select(ApiProject.FromQueryProject); | ||
|
||
return Ok(apiProjects); | ||
} | ||
|
||
|
||
[HttpGet("projects/{projectId:guid}")] | ||
[MapToApiVersion("1.0")] | ||
[ProducesResponseType(StatusCodes.Status200OK)] | ||
[ProducesResponseType(StatusCodes.Status404NotFound)] | ||
public async Task<ActionResult<ApiProject?>> GetProjectsV1(Guid projectId) | ||
{ | ||
#region Authorization | ||
|
||
var authResult = await Request.RequireAuthorizationAsync(r => | ||
{ | ||
r.AlwaysAccessWhen().ResourcesFullControl(); | ||
r.AnyOf(or => { or.BeTrustedApplication(); }); | ||
}); | ||
|
||
if (authResult.Unauthorized) | ||
return authResult.CreateForbiddenResponse(); | ||
|
||
#endregion Authorization | ||
|
||
var projects = await DispatchAsync(new GetProjects().WhereProjectId(projectId)); | ||
|
||
var apiProjects = projects.Select(ApiProject.FromQueryProject); | ||
|
||
return Ok(apiProjects.FirstOrDefault()); | ||
} | ||
|
||
[HttpPut("projects/{projectId:guid}")] | ||
[MapToApiVersion("1.0")] | ||
[ProducesResponseType(StatusCodes.Status201Created)] | ||
[ProducesResponseType(StatusCodes.Status204NoContent)] | ||
[ProducesResponseType(StatusCodes.Status400BadRequest)] | ||
public async Task<ActionResult<ApiProject?>> PutProjectsV1(Guid projectId, PutProjectRequest request) | ||
{ | ||
#region Authorization | ||
|
||
var authResult = await Request.RequireAuthorizationAsync(r => | ||
{ | ||
r.AlwaysAccessWhen().ResourcesFullControl(); | ||
r.AnyOf(or => { or.BeTrustedApplication(); }); | ||
}); | ||
|
||
if (authResult.Unauthorized) | ||
return authResult.CreateForbiddenResponse(); | ||
|
||
#endregion Authorization | ||
|
||
var personIdentifiers = request.AssignedAdminsAzureUniqueId | ||
.Select(p => new PersonIdentifier(p)); | ||
|
||
if (request.DirectorAzureUniqueId.HasValue) | ||
personIdentifiers = personIdentifiers.Append(new PersonIdentifier(request.DirectorAzureUniqueId.Value)); | ||
|
||
var unresolvedProfiles = (await ResolvePersonsAsync(personIdentifiers)) | ||
.Where(r => !r.Success) | ||
.ToList(); | ||
|
||
if (unresolvedProfiles.Count != 0) | ||
return FusionApiError.NotFound(string.Join(',', unresolvedProfiles), "Profiles could not be resolved"); | ||
|
||
|
||
var project = (await DispatchAsync(new GetProjects().WhereProjectId(projectId))).FirstOrDefault(); | ||
|
||
if (project == null) | ||
{ | ||
await DispatchAsync(new CreateProject(request)); | ||
|
||
return Created(Request.GetUri(), null); | ||
} | ||
|
||
await DispatchAsync(new UpdateProject(project.Id, request)); | ||
|
||
return NoContent(); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/Fusion.Summary.Api/Controllers/Requests/PutProjectRequest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using FluentValidation; | ||
|
||
namespace Fusion.Summary.Api.Controllers.Requests; | ||
|
||
public class PutProjectRequest | ||
{ | ||
public required string Name { get; set; } | ||
public required Guid OrgProjectExternalId { get; set; } | ||
|
||
public Guid? DirectorAzureUniqueId { get; set; } | ||
|
||
public Guid[] AssignedAdminsAzureUniqueId { get; set; } = []; | ||
|
||
|
||
public class Validator : AbstractValidator<PutProjectRequest> | ||
{ | ||
public Validator() | ||
{ | ||
RuleFor(x => x.Name).NotEmpty(); | ||
RuleFor(x => x.OrgProjectExternalId).NotEmpty(); | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
src/Fusion.Summary.Api/Controllers/Requests/PutWeeklyTaskOwnerReportRequest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using FluentValidation; | ||
|
||
namespace Fusion.Summary.Api.Controllers.Requests; | ||
|
||
public class PutWeeklyTaskOwnerReportRequest | ||
{ | ||
public DateTime PeriodStart { get; set; } | ||
public DateTime PeriodEnd { get; set; } | ||
|
||
|
||
public class Validator : AbstractValidator<PutWeeklyTaskOwnerReportRequest> | ||
{ | ||
public Validator() | ||
{ | ||
RuleFor(x => x.PeriodStart).NotEmpty(); | ||
RuleFor(x => x.PeriodEnd).NotEmpty(); | ||
RuleFor(x => x.PeriodStart).LessThan(x => x.PeriodEnd); | ||
RuleFor(x => x.PeriodStart).Must(x => x.DayOfWeek == DayOfWeek.Monday).WithMessage("Period start must be a Monday"); | ||
RuleFor(x => x.PeriodEnd).Must(x => x.DayOfWeek == DayOfWeek.Monday).WithMessage("Period end must be a Monday"); | ||
|
||
RuleFor(x => x) | ||
.Must(x => x.PeriodEnd.Date == x.PeriodStart.Date.AddDays(7)) | ||
.WithMessage("Period must be exactly 7 days"); | ||
} | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
src/Fusion.Summary.Api/Controllers/TaskOwnerSummaryController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
using System.Net.Mime; | ||
using Fusion.AspNetCore.FluentAuthorization; | ||
using Fusion.AspNetCore.OData; | ||
using Fusion.Authorization; | ||
using Fusion.Summary.Api.Authorization.Extensions; | ||
using Fusion.Summary.Api.Controllers.ApiModels; | ||
using Fusion.Summary.Api.Controllers.Requests; | ||
using Fusion.Summary.Api.Domain.Commands; | ||
using Fusion.Summary.Api.Domain.Queries; | ||
using Microsoft.ApplicationInsights.AspNetCore.Extensions; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
namespace Fusion.Summary.Api.Controllers; | ||
|
||
[Authorize] | ||
[ApiController] | ||
[Produces(MediaTypeNames.Application.Json)] | ||
[ApiVersion("1.0")] | ||
public class TaskOwnerSummaryController : BaseController | ||
{ | ||
[HttpGet("task-owners-summary-reports/{projectId:guid}/weekly")] | ||
[MapToApiVersion("1.0")] | ||
[ProducesResponseType(StatusCodes.Status200OK)] | ||
[ProducesResponseType(StatusCodes.Status404NotFound)] | ||
[ProducesResponseType(StatusCodes.Status400BadRequest)] | ||
[ODataTop(100), ODataSkip] | ||
public async Task<ActionResult<ApiCollection<ApiWeeklyTaskOwnerReport>>> GetWeeklyTaskOwnerReportsV1(Guid projectId, ODataQueryParams query) | ||
{ | ||
#region Authorization | ||
|
||
var authResult = | ||
await Request.RequireAuthorizationAsync(r => | ||
{ | ||
r.AlwaysAccessWhen().ResourcesFullControl(); | ||
r.AnyOf(or => { or.BeTrustedApplication(); }); | ||
}); | ||
|
||
if (authResult.Unauthorized) | ||
return authResult.CreateForbiddenResponse(); | ||
|
||
#endregion | ||
|
||
if ((await DispatchAsync(new GetProjects().WhereProjectId(projectId))).FirstOrDefault() is null) | ||
return ProjectNotFound(projectId); | ||
|
||
|
||
var projects = await DispatchAsync(new GetWeeklyTaskOwnerReports(projectId, query)); | ||
|
||
return Ok(ApiCollection<ApiWeeklyTaskOwnerReport>.FromQueryCollection(projects, ApiWeeklyTaskOwnerReport.FromQueryWeeklyTaskOwnerReport)); | ||
} | ||
|
||
[HttpGet("task-owners-summary-reports/{projectId:guid}/weekly/{reportId:guid}")] | ||
[MapToApiVersion("1.0")] | ||
[ProducesResponseType(StatusCodes.Status200OK)] | ||
[ProducesResponseType(StatusCodes.Status404NotFound)] | ||
public async Task<ActionResult<ApiWeeklyTaskOwnerReport?>> GetWeeklyTaskOwnerReportV1(Guid projectId, Guid reportId) | ||
{ | ||
#region Authorization | ||
|
||
var authResult = | ||
await Request.RequireAuthorizationAsync(r => | ||
{ | ||
r.AlwaysAccessWhen().ResourcesFullControl(); | ||
r.AnyOf(or => { or.BeTrustedApplication(); }); | ||
}); | ||
|
||
if (authResult.Unauthorized) | ||
return authResult.CreateForbiddenResponse(); | ||
|
||
#endregion | ||
|
||
if ((await DispatchAsync(new GetProjects().WhereProjectId(projectId))).FirstOrDefault() is null) | ||
return ProjectNotFound(projectId); | ||
|
||
var report = (await DispatchAsync(new GetWeeklyTaskOwnerReports(projectId, new ODataQueryParams()).WhereReportId(reportId))).FirstOrDefault(); | ||
|
||
return report is null ? NotFound() : Ok(ApiWeeklyTaskOwnerReport.FromQueryWeeklyTaskOwnerReport(report)); | ||
} | ||
|
||
/// <summary> | ||
/// Summary report key is composed of the project id and the period start and end dates. | ||
/// If a report already exists for the given period and project id, it will be replaced. | ||
/// </summary> | ||
[HttpPut("task-owners-summary-reports/{projectId:guid}/weekly")] | ||
[MapToApiVersion("1.0")] | ||
[ProducesResponseType(StatusCodes.Status201Created)] | ||
[ProducesResponseType(StatusCodes.Status204NoContent)] | ||
[ProducesResponseType(StatusCodes.Status400BadRequest)] | ||
public async Task<IActionResult> PutWeeklyTaskOwnerReportV1(Guid projectId, [FromBody] PutWeeklyTaskOwnerReportRequest request) | ||
{ | ||
#region Authorization | ||
|
||
var authResult = | ||
await Request.RequireAuthorizationAsync(r => | ||
{ | ||
r.AlwaysAccessWhen().ResourcesFullControl(); | ||
r.AnyOf(or => { or.BeTrustedApplication(); }); | ||
}); | ||
|
||
if (authResult.Unauthorized) | ||
return authResult.CreateForbiddenResponse(); | ||
|
||
#endregion | ||
|
||
var project = (await DispatchAsync(new GetProjects().WhereProjectId(projectId))).FirstOrDefault(); | ||
if (project is null) | ||
return ProjectNotFound(projectId); | ||
|
||
var command = new PutWeeklyTaskOwnerReport(project.Id, request); | ||
|
||
var newReportCreated = await DispatchAsync(command); | ||
|
||
return newReportCreated ? Created(Request.GetUri(), null) : NoContent(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.