diff --git a/src/InternshipSystem.Api/ApiProfile.cs b/src/InternshipSystem.Api/ApiProfile.cs index 9f20a90..efbe56d 100644 --- a/src/InternshipSystem.Api/ApiProfile.cs +++ b/src/InternshipSystem.Api/ApiProfile.cs @@ -18,12 +18,22 @@ namespace InternshipSystem.Api .ForMember( result => result.Status, opt => opt.MapFrom(edition => edition.IsOpen ? "Open" : "Archival")); + + CreateMap(); + + CreateMap(); CreateMap(); CreateMap(); - CreateMap().IncludeMembers(es => es.Subject); + CreateMap() + .IncludeMembers(es => es.Subject); + + CreateMap() + .IncludeMembers(eit => eit.InternshipType); + + CreateMap(); } } } \ No newline at end of file diff --git a/src/InternshipSystem.Api/Controllers/EditionManagementController.cs b/src/InternshipSystem.Api/Controllers/EditionManagementController.cs new file mode 100644 index 0000000..71cecc2 --- /dev/null +++ b/src/InternshipSystem.Api/Controllers/EditionManagementController.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using InternshipSystem.Api.Queries.SearchQuery; +using InternshipSystem.Api.Result; +using InternshipSystem.Api.Security; +using InternshipSystem.Repository; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace InternshipSystem.Api.Controllers +{ + [ApiController] + [Route("editionManagement")] + public class EditionManagementController : ControllerBase + { + private InternshipDbContext Context { get; } + private IMapper Mapper { get; } + + public EditionManagementController(IMapper mapper, InternshipDbContext context) + { + Context = context; + Mapper = mapper; + } + + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [Authorize(Policy = Policies.IsOverseer)] + public async Task>> GetEditions([FromQuery] EditionSearchQuery searchQuery, CancellationToken token) => + await Context.Editions + .Include(e => e.Course) + .ProjectTo(Mapper.ConfigurationProvider) + .Skip(searchQuery.Page * searchQuery.PerPage) + .Take(searchQuery.PerPage) + .ToListAsync(token); + + [HttpGet("{editionId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [Authorize(Policy = Policies.IsOverseer)] + public async Task> GetFullEdition(CancellationToken token) + { + var edition = await Context.Editions + .Include(e => e.Course) + .Include(e => e.AvailableSubjects) + .ThenInclude(s => s.Subject) + .Include(e => e.AvailableInternshipTypes) + .ThenInclude(i => i.InternshipType) + .ProjectTo(Mapper.ConfigurationProvider) + .FirstOrDefaultAsync(token); + + if (edition == null) + { + return NotFound(); + } + + return Ok(edition); + } + + [HttpPut] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [Authorize(Policy = Policies.IsOverseer)] + public async Task UpsertEdition(EditionForm editionForm, CancellationToken token) + { + var validator = new EditionForm.Validator(); + // TODO: complete validation rules + var validationResult = await validator.ValidateAsync(editionForm, token); + + if (!validationResult.IsValid) + { + return BadRequest(validationResult.ToString()); + } + + // TODO: complete add/update + + return Ok(); + } + + [HttpDelete("{editionId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + [Authorize(Policy = Policies.IsOverseer)] + public async Task DeleteEdition(Guid editionId, CancellationToken token) + { + var editionToDelete = await Context.Editions + .Include(e => e.AvailableSubjects) + .Include(e => e.AvailableInternshipTypes) + .FirstOrDefaultAsync(e => e.Id.Equals(editionId), token); + + if (editionToDelete == null) + { + return NotFound(); + } + + var referencedInternships = + await Context + .Entry(editionToDelete) + .Collection(e => e.Internships) + .Query() + .CountAsync(token); + + if (referencedInternships > 0) + { + return Conflict(); + } + + Context.Editions.Attach(editionToDelete); + Context.Editions.Remove(editionToDelete); + await Context.SaveChangesAsync(token); + return Ok(); + } + } +} \ No newline at end of file diff --git a/src/InternshipSystem.Api/Queries/EditionForm.cs b/src/InternshipSystem.Api/Queries/EditionForm.cs new file mode 100644 index 0000000..3b41aac --- /dev/null +++ b/src/InternshipSystem.Api/Queries/EditionForm.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using FluentValidation; +using InternshipSystem.Core; +using InternshipSystem.Core.Entity.Internship; + +namespace InternshipSystem.Api.Controllers +{ + public class EditionForm + { + public Guid Id { get; set; } + public DateTime EditionStart { get; set; } + public DateTime EditionFinish { get; set; } + public DateTime ReportingStart { get; set; } + public Course Course { get; set; } + public List AvailableSubjects { get; set; } + public List AvailableInternshipTypes { get; set; } + + public class Validator : AbstractValidator + { + public Validator() + { + //TODO: later + } + } + } +} \ No newline at end of file diff --git a/src/InternshipSystem.Api/Queries/SearchQuery/EditionSearchQuery.cs b/src/InternshipSystem.Api/Queries/SearchQuery/EditionSearchQuery.cs new file mode 100644 index 0000000..3495ecf --- /dev/null +++ b/src/InternshipSystem.Api/Queries/SearchQuery/EditionSearchQuery.cs @@ -0,0 +1,7 @@ +namespace InternshipSystem.Api.Queries.SearchQuery +{ + public class EditionSearchQuery : SearchQuery + { + + } +} \ No newline at end of file diff --git a/src/InternshipSystem.Api/Result/EditionFullResult.cs b/src/InternshipSystem.Api/Result/EditionFullResult.cs new file mode 100644 index 0000000..57c9e29 --- /dev/null +++ b/src/InternshipSystem.Api/Result/EditionFullResult.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using InternshipSystem.Core; +using InternshipSystem.Core.Entity.Internship; + +namespace InternshipSystem.Api.Result +{ + public class EditionFullResult + { + public Guid Id { get; set; } + public DateTime EditionStart { get; set; } + public DateTime EditionFinish { get; set; } + public DateTime ReportingStart { get; set; } + public Course Course { get; set; } + public List AvailableSubjects { get; set; } + public List AvailableInternshipTypes { get; set; } + } +} \ No newline at end of file diff --git a/src/InternshipSystem.Api/Result/EditionManagementResult.cs b/src/InternshipSystem.Api/Result/EditionManagementResult.cs new file mode 100644 index 0000000..2e86aa3 --- /dev/null +++ b/src/InternshipSystem.Api/Result/EditionManagementResult.cs @@ -0,0 +1,14 @@ +using System; +using InternshipSystem.Core; + +namespace InternshipSystem.Api.Result +{ + public class EditionManagementResult + { + public Guid Id { get; set; } + public DateTime EditionStart { get; set; } + public DateTime EditionFinish { get; set; } + public DateTime ReportingStart { get; set; } + public Course Course { get; set; } + } +} \ No newline at end of file diff --git a/src/InternshipSystem.Api/Security/Policies.cs b/src/InternshipSystem.Api/Security/Policies.cs index 58e6d99..2f6a466 100644 --- a/src/InternshipSystem.Api/Security/Policies.cs +++ b/src/InternshipSystem.Api/Security/Policies.cs @@ -3,5 +3,6 @@ public static class Policies { public const string RegisteredOnly = "RegisteredForEditionOnly"; + public const string IsOverseer = "IsOverseer"; } } \ No newline at end of file diff --git a/src/InternshipSystem.Api/Startup.cs b/src/InternshipSystem.Api/Startup.cs index 595b43e..a1b1bed 100644 --- a/src/InternshipSystem.Api/Startup.cs +++ b/src/InternshipSystem.Api/Startup.cs @@ -38,6 +38,8 @@ namespace InternshipSystem.Api .AddAuthorization(o => { o.AddPolicy(Policies.RegisteredOnly, policy => policy.RequireClaim("Edition")); + //TODO: change to claim for InternshipRepresentative + o.AddPolicy(Policies.IsOverseer, policy => policy.RequireClaim("PersonNumber")); }) .AddHttpClient();