feat/documents (#61)

add get scan endpoint

add document scan endpoint

Co-authored-by: MaxchilKH <m.w.bohdanowicz@gmail.com>
This commit is contained in:
maxchil 2020-10-08 19:27:45 +02:00
parent b98ab42c4d
commit 7124b457dc
7 changed files with 131 additions and 105 deletions

View File

@ -1,11 +1,16 @@
using System.Threading;
using System.IO;
using System.Linq;
using System.Net.Mime;
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 +18,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;
}
/// <summary>
@ -34,18 +41,75 @@ namespace InternshipSystem.Api.Controllers
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[Authorize(Policy = Policies.RegisteredOnly)]
public async Task<ActionResult> AddDocumentToInternship([FromBody] DocumentPublishRequest documentRequest,
[FromServices] User user, CancellationToken cancellationToken)
public async Task<ActionResult> 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")]
[Authorize(Policy = Policies.RegisteredOnly)]
public async Task<ActionResult> 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();
}
[HttpGet("{documentId}/scan")]
[Authorize(Policy = Policies.RegisteredOnly)]
public async Task<ActionResult> GetDocumentScan(long documentId, [FromServices] User user, CancellationToken cancellationToken)
{
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);
var document = internship.Documentation.First(d => d.Id == documentId);
var stream = new MemoryStream(document.Scan);
return File(stream, "application/pdf");
}
}
}

View File

@ -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<DocumentPublishRequest>
{
public Validator()
{
RuleFor(document => document.Scan).NotEmpty();
RuleFor(document => document.Type).NotEmpty();
}
}

View File

@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Linq;
namespace InternshipSystem.Api.Service
{
public class FileValidator
{
private readonly Dictionary<string, byte[]> validFileTypes;
public FileValidator()
{
validFileTypes = new Dictionary<string, byte[]> {
{ "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));
}
}
}

View File

@ -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<ActionResult> AddDocumentToInternship(DocumentPublishRequest documentRequest, User user, CancellationToken cancellationToken);
}
}

View File

@ -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<ActionResult> 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<Document>(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();
}
}
}

View File

@ -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<InternshipDbContext>(o =>
o.UseNpgsql(Configuration.GetConnectionString("InternshipDatabase")))
.AddScoped<DatabaseFiller>()
.AddScoped<IInternshipService, InternshipService>()
.AddScoped<FileValidator>()
.AddScoped<JwtTokenService>()
.AddAutoMapper(cfg => cfg.AddProfile<ApiProfile>());

View File

@ -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;
}
}
}