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 cf8b09a0b5ee0e41b6da2ed00e96d89a546e665c Mon Sep 17 00:00:00 2001 From: MaxchilKH Date: Sun, 18 Oct 2020 08:55:47 +0200 Subject: [PATCH 2/2] scan fix --- .../Controllers/DocumentsController.cs | 14 +++++++------- src/InternshipSystem.Core/Entity/Document.cs | 9 ++++++++- .../Entity/Internship/Internship.cs | 4 ++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/InternshipSystem.Api/Controllers/DocumentsController.cs b/src/InternshipSystem.Api/Controllers/DocumentsController.cs index 2f59065..7cda5aa 100644 --- a/src/InternshipSystem.Api/Controllers/DocumentsController.cs +++ b/src/InternshipSystem.Api/Controllers/DocumentsController.cs @@ -1,6 +1,5 @@ using System.IO; using System.Linq; -using System.Net.Mime; using System.Threading; using System.Threading.Tasks; using InternshipSystem.Api.Queries; @@ -61,9 +60,11 @@ namespace InternshipSystem.Api.Controllers await _context.Entry(edition) .Collection(e => e.Internships) .Query() + .Include(i => i.Documentation) .FirstAsync(i => i.Student.Id == user.PersonNumber, cancellationToken); internship.AddNewDocument(documentRequest.Description, documentRequest.Type); + await _context.SaveChangesAsync(cancellationToken); return Ok(); } @@ -86,14 +87,12 @@ namespace InternshipSystem.Api.Controllers await _context.Entry(edition) .Collection(e => e.Internships) .Query() + .Include(i => i.Documentation) .FirstAsync(i => i.Student.Id == user.PersonNumber, cancellationToken); - var document = await _context.Entry(internship) - .Collection(i => i.Documentation) - .Query() - .FirstOrDefaultAsync(d => d.Id == documentId, cancellationToken); + var document = internship.Documentation.First(d => d.Id == documentId); - document.Scan = memoryStream.ToArray(); + document.Scan = new DocumentScan { File = memoryStream.ToArray() }; document.State = DocumentState.Submitted; await _context.SaveChangesAsync(cancellationToken); @@ -117,9 +116,10 @@ namespace InternshipSystem.Api.Controllers await _context.Entry(internship) .Collection(i => i.Documentation) .Query() + .Include(d => d.Scan) .FirstOrDefaultAsync(d => d.Id == documentId, cancellationToken); - var stream = new MemoryStream(document.Scan); + var stream = new MemoryStream(document.Scan.File); return File(stream, "application/pdf"); } diff --git a/src/InternshipSystem.Core/Entity/Document.cs b/src/InternshipSystem.Core/Entity/Document.cs index 3cda75c..d98bb74 100644 --- a/src/InternshipSystem.Core/Entity/Document.cs +++ b/src/InternshipSystem.Core/Entity/Document.cs @@ -6,9 +6,16 @@ namespace InternshipSystem.Core { public long Id { get; set; } public string Description { get; set; } - public byte[] Scan { get; set; } + public DocumentScan Scan { get; set; } public DocumentType Type { get; set; } public DocumentState State { get; set; } public string RejectionReason { get; set; } } + + public class DocumentScan + { + public long DocumentId { get; set; } + public Document Document { get; set; } + public byte[] File { get; set; } + } } \ No newline at end of file diff --git a/src/InternshipSystem.Core/Entity/Internship/Internship.cs b/src/InternshipSystem.Core/Entity/Internship/Internship.cs index 91ff865..3d148f4 100644 --- a/src/InternshipSystem.Core/Entity/Internship/Internship.cs +++ b/src/InternshipSystem.Core/Entity/Internship/Internship.cs @@ -45,8 +45,8 @@ namespace InternshipSystem.Core.Entity.Internship { var document = Documentation.First(d => d.Id == documentId); - document.Scan = documentScan; - document.State = DocumentState.Submitted; + // document.Scan = documentScan; + // document.State = DocumentState.Submitted; } } } \ No newline at end of file -- 2.45.2