From b1db47726a10f90dc6c75778200d805acc5c5d17 Mon Sep 17 00:00:00 2001 From: MaxchilKH Date: Thu, 8 Oct 2020 19:18:57 +0200 Subject: [PATCH 1/2] add document scan endpoint --- .../Controllers/DocumentsController.cs | 66 +++++++++++++++---- .../Queries/DocumentPublishRequest.cs | 3 +- .../Service/FileValidator.cs | 30 +++++++++ .../Services/IInternshipService.cs | 13 ---- .../Services/InternshipService.cs | 58 ---------------- src/InternshipSystem.Api/Startup.cs | 4 +- .../Entity/Internship/Internship.cs | 40 ++++++----- 7 files changed, 109 insertions(+), 105 deletions(-) create mode 100644 src/InternshipSystem.Api/Service/FileValidator.cs delete mode 100644 src/InternshipSystem.Api/Services/IInternshipService.cs delete mode 100644 src/InternshipSystem.Api/Services/InternshipService.cs diff --git a/src/InternshipSystem.Api/Controllers/DocumentsController.cs b/src/InternshipSystem.Api/Controllers/DocumentsController.cs index c88113b..d502fb7 100644 --- a/src/InternshipSystem.Api/Controllers/DocumentsController.cs +++ b/src/InternshipSystem.Api/Controllers/DocumentsController.cs @@ -1,11 +1,14 @@ -using System.Threading; +using System.IO; +using System.Threading; using System.Threading.Tasks; using InternshipSystem.Api.Queries; using InternshipSystem.Api.Security; -using InternshipSystem.Api.Services; +using InternshipSystem.Api.Service; +using InternshipSystem.Repository; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace InternshipSystem.Api.Controllers { @@ -13,11 +16,13 @@ namespace InternshipSystem.Api.Controllers [Route("document")] public class DocumentsController : ControllerBase { - private readonly IInternshipService _internshipService; + private readonly InternshipDbContext _context; + private readonly FileValidator _fileValidator; - public DocumentsController(IInternshipService internshipService) + public DocumentsController(InternshipDbContext context, FileValidator fileValidator) { - _internshipService = internshipService; + _context = context; + _fileValidator = fileValidator; } /// @@ -34,18 +39,55 @@ namespace InternshipSystem.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [Authorize(Policy = Policies.RegisteredOnly)] - public async Task AddDocumentToInternship([FromBody] DocumentPublishRequest documentRequest, - [FromServices] User user, CancellationToken cancellationToken) + public async Task AddDocumentToInternship( + [FromBody] DocumentPublishRequest documentRequest, + [FromServices] User user, + CancellationToken cancellationToken) { var validator = new DocumentPublishRequest.Validator(); - var validationResult = await validator.ValidateAsync(documentRequest, cancellationToken); - - if (!validationResult.IsValid) + var result = await validator.ValidateAsync(documentRequest, cancellationToken); + + if (!result.IsValid) { - return BadRequest(validationResult.ToString()); + return BadRequest(result.ToString()); } - return await _internshipService.AddDocumentToInternship(documentRequest, user, cancellationToken); + var edition = await _context.Editions.FirstAsync(e => e.Id == user.EditionId, cancellationToken); + + var internship = + await _context.Entry(edition) + .Collection(e => e.Internships) + .Query() + .FirstAsync(i => i.Student.Id == user.PersonNumber, cancellationToken); + + internship.AddNewDocument(documentRequest.Description, documentRequest.Type); + + return Ok(); + } + + [HttpPut("{documentId}/scan")] + public async Task AddDocumentScan(long documentId, IFormFile documentScan, [FromServices] User user, CancellationToken cancellationToken) + { + await using var memoryStream = new MemoryStream(); + await documentScan.CopyToAsync(memoryStream, cancellationToken); + + if (!_fileValidator.IsValidFile(memoryStream.ToArray())) + { + return BadRequest("error.document.scan"); + } + + var edition = await _context.Editions.FirstAsync(e => e.Id == user.EditionId, cancellationToken); + + var internship = + await _context.Entry(edition) + .Collection(e => e.Internships) + .Query() + .Include(i => i.Documentation) + .FirstAsync(i => i.Student.Id == user.PersonNumber, cancellationToken); + + internship.UpdateDocumentScan(documentId, memoryStream.ToArray()); + + return Ok(); } } } \ No newline at end of file diff --git a/src/InternshipSystem.Api/Queries/DocumentPublishRequest.cs b/src/InternshipSystem.Api/Queries/DocumentPublishRequest.cs index 975b704..272775a 100644 --- a/src/InternshipSystem.Api/Queries/DocumentPublishRequest.cs +++ b/src/InternshipSystem.Api/Queries/DocumentPublishRequest.cs @@ -1,5 +1,6 @@ using FluentValidation; using InternshipSystem.Core.ValueObject; +using Microsoft.AspNetCore.Http; namespace InternshipSystem.Api.Queries { @@ -7,14 +8,12 @@ namespace InternshipSystem.Api.Queries { public long? Id { get; set; } public string Description { get; set; } - public byte[] Scan { get; set; } public DocumentType Type { get; set; } public class Validator : AbstractValidator { public Validator() { - RuleFor(document => document.Scan).NotEmpty(); RuleFor(document => document.Type).NotEmpty(); } } diff --git a/src/InternshipSystem.Api/Service/FileValidator.cs b/src/InternshipSystem.Api/Service/FileValidator.cs new file mode 100644 index 0000000..838081f --- /dev/null +++ b/src/InternshipSystem.Api/Service/FileValidator.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; + +namespace InternshipSystem.Api.Service +{ + public class FileValidator + { + private readonly Dictionary validFileTypes; + + public FileValidator() + { + validFileTypes = new Dictionary { + { "pdf", new byte[] { 0x25, 0x50, 0x44, 0x46 } } + }; + } + + public bool IsValidFile(byte[] scan) + { + return IsFileValidType(scan); + } + + private bool IsFileValidType(byte[] scan) + { + var validSignatures = validFileTypes.Values; + var header = scan[..4]; + + return validSignatures.Any(sig => header.SequenceEqual(header)); + } + } +} \ No newline at end of file diff --git a/src/InternshipSystem.Api/Services/IInternshipService.cs b/src/InternshipSystem.Api/Services/IInternshipService.cs deleted file mode 100644 index e3a7294..0000000 --- a/src/InternshipSystem.Api/Services/IInternshipService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using InternshipSystem.Api.Queries; -using InternshipSystem.Api.Security; -using Microsoft.AspNetCore.Mvc; - -namespace InternshipSystem.Api.Services -{ - public interface IInternshipService - { - Task AddDocumentToInternship(DocumentPublishRequest documentRequest, User user, CancellationToken cancellationToken); - } -} \ No newline at end of file diff --git a/src/InternshipSystem.Api/Services/InternshipService.cs b/src/InternshipSystem.Api/Services/InternshipService.cs deleted file mode 100644 index 3ea6a7a..0000000 --- a/src/InternshipSystem.Api/Services/InternshipService.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using AutoMapper; -using InternshipSystem.Api.Queries; -using InternshipSystem.Api.Security; -using InternshipSystem.Core; -using InternshipSystem.Repository; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace InternshipSystem.Api.Services -{ - public class InternshipService : IInternshipService - { - private readonly InternshipDbContext _context; - private IMapper Mapper { get; } - - public InternshipService(InternshipDbContext context, IMapper mapper) - { - _context = context; - Mapper = mapper; - } - - public async Task AddDocumentToInternship(DocumentPublishRequest documentRequest, User user, - CancellationToken cancellationToken) - { - var edition = await _context.Editions.FindAsync(user.EditionId); - - var internship = await _context.Entry(edition) - .Collection(e => e.Internships) - .Query() - .Include(i => i.Documentation) - .SingleAsync(i => i.Student.Id == user.PersonNumber, cancellationToken); - - var document = Mapper.Map(documentRequest); - - if (documentRequest.Id.HasValue) - { - try - { - internship.UpdateDocument(document); - } - catch (InvalidOperationException) - { - return new NotFoundResult(); - } - } - else - { - internship.AddNewDocument(document); - } - - await _context.SaveChangesAsync(cancellationToken); - return new OkResult(); - } - } -} \ No newline at end of file diff --git a/src/InternshipSystem.Api/Startup.cs b/src/InternshipSystem.Api/Startup.cs index 916c84e..2eddcfe 100644 --- a/src/InternshipSystem.Api/Startup.cs +++ b/src/InternshipSystem.Api/Startup.cs @@ -7,7 +7,7 @@ using InternshipSystem.Api.Extensions; using InternshipSystem.Api.ModelBinders; using InternshipSystem.Api.Options; using InternshipSystem.Api.Security; -using InternshipSystem.Api.Services; +using InternshipSystem.Api.Service; using InternshipSystem.Repository; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -44,7 +44,7 @@ namespace InternshipSystem.Api .AddDbContext(o => o.UseNpgsql(Configuration.GetConnectionString("InternshipDatabase"))) .AddScoped() - .AddScoped() + .AddScoped() .AddScoped() .AddAutoMapper(cfg => cfg.AddProfile()); diff --git a/src/InternshipSystem.Core/Entity/Internship/Internship.cs b/src/InternshipSystem.Core/Entity/Internship/Internship.cs index 10eafea..8251873 100644 --- a/src/InternshipSystem.Core/Entity/Internship/Internship.cs +++ b/src/InternshipSystem.Core/Entity/Internship/Internship.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using InternshipSystem.Core.ValueObject; namespace InternshipSystem.Core.Entity.Internship { @@ -14,24 +15,7 @@ namespace InternshipSystem.Core.Entity.Internship public Edition Edition { get; set; } - public float? Grade { get; set; } - - public void UpdateDocument(Document document) - { - var oldDocument = Documentation.First(d => d.Id == document.Id); - - oldDocument.Description = document.Description ?? oldDocument.Description; - oldDocument.Scan = document.Scan ?? oldDocument.Scan; - oldDocument.Type = document.Type; - oldDocument.State = DocumentState.Submitted; - } - - public void AddNewDocument(Document document) - { - document.State = DocumentState.Submitted; - - Documentation.Add(document); - } + public float? Grade { get; set; } public static Internship CreateStudentsInternship(Student student) { @@ -46,5 +30,25 @@ namespace InternshipSystem.Core.Entity.Internship return internship; } + + public void AddNewDocument(string description, DocumentType type) + { + var document = new Document + { + Description = description, + Type = type, + State = DocumentState.Draft + }; + + Documentation.Add(document); + } + + public void UpdateDocumentScan(long documentId, byte[] documentScan) + { + var document = Documentation.First(d => d.Id == documentId); + + document.Scan = documentScan; + document.State = DocumentState.Submitted; + } } } \ No newline at end of file -- 2.45.2 From 374d2812c1fa5e500667e5cd456293e7c28428f1 Mon Sep 17 00:00:00 2001 From: MaxchilKH Date: Thu, 8 Oct 2020 21:56:53 +0200 Subject: [PATCH 2/2] Add hours to registration --- src/InternshipSystem.Api/Commands/UpdateRegistrationForm.cs | 1 + .../UseCases/UpdateInternshipRegistrationUseCase.cs | 1 + .../Entity/Internship/InternshipRegistration.cs | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/InternshipSystem.Api/Commands/UpdateRegistrationForm.cs b/src/InternshipSystem.Api/Commands/UpdateRegistrationForm.cs index 5bcb1be..61a35af 100644 --- a/src/InternshipSystem.Api/Commands/UpdateRegistrationForm.cs +++ b/src/InternshipSystem.Api/Commands/UpdateRegistrationForm.cs @@ -13,6 +13,7 @@ namespace InternshipSystem.Api.Commands public DateTime? End { get; set; } public UpdateMentor? Mentor { get; set; } public List Subjects { get; set; } + public int? Hours { get; set; } public long? Type { get; set; } } diff --git a/src/InternshipSystem.Api/UseCases/UpdateInternshipRegistrationUseCase.cs b/src/InternshipSystem.Api/UseCases/UpdateInternshipRegistrationUseCase.cs index c3b49a5..a490cf4 100644 --- a/src/InternshipSystem.Api/UseCases/UpdateInternshipRegistrationUseCase.cs +++ b/src/InternshipSystem.Api/UseCases/UpdateInternshipRegistrationUseCase.cs @@ -36,6 +36,7 @@ namespace InternshipSystem.Api.UseCases { subjectRegistration.Start = registrationCommand.Start ?? subjectRegistration.Start; subjectRegistration.End = registrationCommand.End ?? subjectRegistration.End; + subjectRegistration.DeclaredHours = registrationCommand.Hours ?? subjectRegistration.DeclaredHours; if (registrationCommand.Type.HasValue) { diff --git a/src/InternshipSystem.Core/Entity/Internship/InternshipRegistration.cs b/src/InternshipSystem.Core/Entity/Internship/InternshipRegistration.cs index db6b877..d220a42 100644 --- a/src/InternshipSystem.Core/Entity/Internship/InternshipRegistration.cs +++ b/src/InternshipSystem.Core/Entity/Internship/InternshipRegistration.cs @@ -16,8 +16,10 @@ namespace InternshipSystem.Core.Entity.Internship public Mentor Mentor { get; set; } public List Subjects { get; set; } public InternshipType Type { get; set; } + + public int DeclaredHours { get; set; } public DocumentState State { get; set; } - + public static InternshipRegistration Create() { return new InternshipRegistration(); -- 2.45.2