Справки по страницам

1. Добавил модель данных
2. Добавил Dto для справки
3. Добавил доменный сервис + сделал покрытие тестами
4. Добавил репозиторий для справки
5. Сделал регистрацию зависимостей
6. Добавил контроллер содержащий методы: создания, редактирования, получения файла справки
This commit is contained in:
parent 2769271583
commit cd279b925f
9 changed files with 706 additions and 0 deletions

View File

@ -0,0 +1,33 @@
using System.ComponentModel.DataAnnotations;
namespace AsbCloudApp.Data;
public class HelpPageDto : IId
{
/// <summary>
/// Id записи
/// </summary>
public int Id { get; set; }
/// <summary>
/// Id категории файла
/// </summary>
[Range(1, int.MaxValue, ErrorMessage = "Id категории файла не может быть меньше 1")]
public int IdCategory { get; set; }
/// <summary>
/// Url страницы, которой пренадлежит справка
/// </summary>
public string UrlPage { get; set; } = null!;
/// <summary>
/// Имя файла
/// </summary>
[StringLength(260, MinimumLength = 1, ErrorMessage = "Допустимое имя файла от 1 до 260 символов")]
public string Name { get; set; } = null!;
/// <summary>
/// Размер файла
/// </summary>
public long Size { get; set; }
}

View File

@ -0,0 +1,34 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Repositories;
/// <summary>
/// Интерфейс репозитория справок страниц
/// </summary>
public interface IHelpPageRepository : ICrudRepository<HelpPageDto>
{
/// <summary>
/// Получение справки по url страницы и id категории
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<HelpPageDto?> GetOrDefaultByUrlPageAndIdCategoryAsync(string urlPage,
int idCategory,
CancellationToken cancellationToken);
/// <summary>
/// Проверка на существование справки для определённой страницы в категории
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<bool> IsCheckHelpPageWithUrlPageAndIdCategoryAsync(string urlPage,
int idCategory,
CancellationToken cancellationToken);
}

View File

@ -0,0 +1,70 @@
using AsbCloudApp.Data;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Services;
/// <summary>
/// Интерфейс сервиса справок страниц
/// </summary>
public interface IHelpPageService
{
/// <summary>
/// Создание справки
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="fileName"></param>
/// <param name="fileStream"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<int> CreateAsync(string urlPage,
int idCategory,
string fileName,
Stream fileStream,
CancellationToken cancellationToken);
/// <summary>
/// Редактирование справки
/// </summary>
/// <param name="idCategory"></param>
/// <param name="helpPage"></param>
/// <param name="fileName"></param>
/// <param name="fileStream"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task UpdateAsync(HelpPageDto helpPage,
int idCategory,
string fileName,
Stream fileStream,
CancellationToken cancellationToken);
/// <summary>
/// Получение справки по url страницы и id категории
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<HelpPageDto?> GetOrDefaultByUrlPageAndIdCategoryAsync(string urlPage,
int idCategory,
CancellationToken cancellationToken);
/// <summary>
/// Получение справки по Id
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<HelpPageDto?> GetOrDefaultByIdAsync(int id,
CancellationToken cancellationToken);
/// <summary>
/// Получение файла справки по Id
/// </summary>
/// <param name="helpPage"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Stream GetFileStream(HelpPageDto helpPage);
}

View File

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
namespace AsbCloudDb.Model;
[Table("t_help_page"), Comment("Справки")]
public class HelpPage : IId
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("url_page"), Comment("Url страницы")]
public string UrlPage { get; set; } = null!;
[Column("id_category"), Comment("Id категории файла")]
public int IdCategory { get; set; }
[Column("name"), Comment("Название файла")]
public string Name { get; set; } = null!;
[Column("file_size"), Comment("Размер файла")]
public long Size { get; set; }
[JsonIgnore]
[ForeignKey(nameof(IdCategory))]
public virtual FileCategory FileCategory { get; set; } = null!;
}

View File

@ -136,6 +136,12 @@ namespace AsbCloudInfrastructure
services.AddTransient<ILimitingParameterService, LimitingParameterService>();
services.AddTransient<IProcessMapReportMakerService, ProcessMapReportMakerService>();
services.AddTransient<IProcessMapReportService, ProcessMapReportService>();
services.AddTransient<IHelpPageService, HelpPageService>(serviceProvider =>
new HelpPageService(
serviceProvider.GetRequiredService<IHelpPageRepository>(),
serviceProvider.GetRequiredService<IFileStorageRepository>(),
configuration.GetSection("HelpPageOptions:DirectoryNameHelpPageFiles")
.Get<string>()));
services.AddTransient<TrajectoryService>();
@ -169,6 +175,7 @@ namespace AsbCloudInfrastructure
services.AddTransient<ICrudRepository<DrillerDto>, CrudCacheRepositoryBase<DrillerDto, Driller>>();
services.AddTransient<IHelpPageRepository, HelpPageRepository>();
services.AddTransient<IFileRepository, FileRepository>();
services.AddTransient<IFileStorageRepository, FileStorageRepository>();
services.AddTransient<IWellCompositeRepository, WellCompositeRepository>();

View File

@ -0,0 +1,43 @@
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Repository;
public class HelpPageRepository : CrudRepositoryBase<HelpPageDto, HelpPage>,
IHelpPageRepository
{
public HelpPageRepository(IAsbCloudDbContext context)
: base(context)
{
}
public async Task<HelpPageDto?> GetOrDefaultByUrlPageAndIdCategoryAsync(string urlPage,
int idCategory,
CancellationToken cancellationToken)
{
var helpPage = await dbSet.AsNoTracking()
.SingleOrDefaultAsync(x =>
x.UrlPage == urlPage &&
x.IdCategory == idCategory,
cancellationToken);
if (helpPage is null)
return null;
return helpPage.Adapt<HelpPageDto>();
}
public Task<bool> IsCheckHelpPageWithUrlPageAndIdCategoryAsync(string urlPage, int idCategory,
CancellationToken cancellationToken)
{
return dbSet.AnyAsync(x =>
x.UrlPage == urlPage &&
x.IdCategory == idCategory,
cancellationToken);
}
}

View File

@ -0,0 +1,168 @@
using System;
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Exceptions;
namespace AsbCloudInfrastructure.Services;
/// <summary>
/// Реализация сервиса справок страниц
/// </summary>
public class HelpPageService : IHelpPageService
{
private readonly string directoryNameHelpPageFiles;
private readonly IHelpPageRepository helpPageRepository;
private readonly IFileStorageRepository fileStorageRepository;
/// <summary>
/// Конструктор класса
/// </summary>
/// <param name="helpPageRepository"></param>
/// <param name="fileStorageRepository"></param>
/// <param name="directoryNameHelpPageFiles"></param>
public HelpPageService(IHelpPageRepository helpPageRepository,
IFileStorageRepository fileStorageRepository,
string directoryNameHelpPageFiles)
{
if (string.IsNullOrWhiteSpace(directoryNameHelpPageFiles))
throw new ArgumentException("Value cannot be null or whitespace", nameof(this.directoryNameHelpPageFiles));
this.helpPageRepository = helpPageRepository;
this.fileStorageRepository = fileStorageRepository;
this.directoryNameHelpPageFiles = directoryNameHelpPageFiles;
}
/// <summary>
/// Создание справки страницы
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="fileName"></param>
/// <param name="fileStream"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<int> CreateAsync(string urlPage,
int idCategory,
string fileName,
Stream fileStream,
CancellationToken cancellationToken)
{
if (await helpPageRepository.IsCheckHelpPageWithUrlPageAndIdCategoryAsync(urlPage,
idCategory,
cancellationToken))
{
throw new ArgumentsInvalidException("Справка с такой категории файла для данной страницы уже существует",
new[] { nameof(urlPage), nameof(idCategory) });
}
HelpPageDto helpPage = new()
{
UrlPage = urlPage,
IdCategory = idCategory,
Name = Path.GetFileName(fileName),
Size = fileStream.Length,
};
int idFile = await helpPageRepository.InsertAsync(helpPage,
cancellationToken);
await SaveFileAsync(idCategory,
fileName,
fileStream,
idFile,
cancellationToken);
return idFile;
}
/// <summary>
/// Обновление справки страницы
/// </summary>
/// <param name="helpPage"></param>
/// <param name="idCategory"></param>
/// <param name="fileName"></param>
/// <param name="fileStream"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task UpdateAsync(HelpPageDto helpPage,
int idCategory,
string fileName,
Stream fileStream,
CancellationToken cancellationToken)
{
helpPage.Name = Path.GetFileName(fileName);
helpPage.IdCategory = idCategory;
helpPage.Size = fileStream.Length;
string fileFullName = fileStorageRepository.GetFilePath(directoryNameHelpPageFiles,
idCategory.ToString(),
helpPage.Id,
Path.GetExtension(helpPage.Name));
await helpPageRepository.UpdateAsync(helpPage,
cancellationToken);
fileStorageRepository.DeleteFile(fileFullName);
await SaveFileAsync(helpPage.IdCategory,
fileName,
fileStream,
helpPage.Id,
cancellationToken);
}
/// <summary>
/// Получение справки по url страницы и id категории
/// </summary>
/// <param name="urlPage"></param>
/// <param name="idCategory"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<HelpPageDto?> GetOrDefaultByUrlPageAndIdCategoryAsync(string urlPage,
int idCategory,
CancellationToken cancellationToken) =>
helpPageRepository.GetOrDefaultByUrlPageAndIdCategoryAsync(urlPage,
idCategory,
cancellationToken);
public Task<HelpPageDto?> GetOrDefaultByIdAsync(int id, CancellationToken cancellationToken) =>
helpPageRepository.GetOrDefaultAsync(id,
cancellationToken);
/// <summary>
/// Получение файлового потока для файла справки
/// </summary>
/// <param name="helpPage"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Stream GetFileStream(HelpPageDto helpPage)
{
string filePath = fileStorageRepository.GetFilePath(directoryNameHelpPageFiles,
helpPage.IdCategory.ToString(),
helpPage.Id,
Path.GetExtension(helpPage.Name));;
var fileStream = new FileStream(Path.GetFullPath(filePath), FileMode.Open);
return fileStream;
}
private async Task SaveFileAsync(int idCategory,
string fileName,
Stream fileStream,
int fileId,
CancellationToken cancellationToken = default)
{
string filePath = fileStorageRepository.MakeFilePath(directoryNameHelpPageFiles,
idCategory.ToString(),
$"{fileId}" + $"{Path.GetExtension(fileName)}");
await fileStorageRepository.SaveFileAsync(filePath,
fileStream,
cancellationToken);
}
}

View File

@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudInfrastructure.Services;
using Moq;
using Xunit;
namespace AsbCloudWebApi.Tests.ServicesTests;
public class HelpPageServiceTest
{
private const string directoryNameHelpPageFiles = "helpPages";
private static List<HelpPageDto> HelpPages = new()
{
new()
{
Id = 123,
IdCategory = 20000,
Name = "Ñïðàâêà1.pdf",
Size = 54000,
UrlPage = "test"
},
new()
{
Id = 134,
IdCategory = 20000,
Name = "Ñïðàâêà2.pdf",
Size = 51000,
UrlPage = "test1"
},
new()
{
Id = 178,
IdCategory = 10000,
Name = "Ñïðàâêà3.pdf",
Size = 49000,
UrlPage = "test2"
}
};
private readonly Mock<IHelpPageRepository> helpPageRepository = new();
private readonly Mock<IFileStorageRepository> fileStorageRepository = new();
private readonly IHelpPageService helpPageService;
public HelpPageServiceTest()
{
helpPageService = new HelpPageService(helpPageRepository.Object,
fileStorageRepository.Object,
directoryNameHelpPageFiles);
}
[Fact]
public async Task CreateAsync_ShouldReturn_PositiveId()
{
//arrange
int idHelpPage = new Random().Next(1, 100);
string urlPage = "test";
int idCategory = 20000;
string fullName = "test.pdf";
MemoryStream fileStream = new MemoryStream(Array.Empty<byte>());
helpPageRepository.Setup(x => x.InsertAsync(It.IsAny<HelpPageDto>(),
It.IsAny<CancellationToken>()))
.Returns(() => Task.FromResult(idHelpPage));
fileStorageRepository.Setup(x => x.SaveFileAsync(It.IsAny<string>(),
It.IsAny<Stream>(),
It.IsAny<CancellationToken>()));
//act
int result = await helpPageService.CreateAsync(urlPage,
idCategory,
fullName,
fileStream,
CancellationToken.None);
//assert
Assert.True(result > 0);
}
[Fact]
public async Task CreateAsync_ShouldReturn_ArgumentsInvalidException()
{
//arrange
string urlPage = "test";
int idCategory = 20000;
string fullName = "test.pdf";
MemoryStream fileStream = new MemoryStream(Array.Empty<byte>());
bool isExistingHelpPage = true;
helpPageRepository.Setup(x => x.IsCheckHelpPageWithUrlPageAndIdCategoryAsync(It.IsAny<string>(),
It.IsAny<int>(),
It.IsAny<CancellationToken>()))
.Returns(() => Task.FromResult(isExistingHelpPage));
//act
Task Result () => helpPageService.CreateAsync(urlPage,
idCategory,
fullName,
fileStream,
CancellationToken.None);
//assert
await Assert.ThrowsAsync<ArgumentsInvalidException>(Result);
}
[Fact]
public async Task UpdateAsync_ShouldReturn_UpdatedHelpPage()
{
//arrange
HelpPageDto helpPage = new()
{
Id = 123,
IdCategory = 134,
UrlPage = "test",
Name = "Ñïðàâêà.pdf",
Size = 54000
};
int newIdCategory = 451;
string newFileName = "Íîâàÿ ñïðàâêà.pdf";
MemoryStream newFileStream = new MemoryStream(Array.Empty<byte>());
//act
await helpPageService.UpdateAsync(helpPage,
newIdCategory,
newFileName,
newFileStream,
CancellationToken.None);
//assert
Assert.Equal(newFileName, helpPage.Name);
Assert.Equal(newIdCategory, helpPage.IdCategory);
Assert.Equal(newFileStream.Length, helpPage.Size);
}
[Theory]
[InlineData(20000, "test")]
[InlineData(20000, "test1")]
public async Task GetOrDefaultByUrlPageAndIdCategoryAsync_ShouldReturn_HelpPageDto(int idCategory,
string urlPage)
{
//arrange
helpPageRepository.Setup(x => x.GetOrDefaultByUrlPageAndIdCategoryAsync(It.IsAny<string>(),
It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(() =>
{
var helpPage = HelpPages.FirstOrDefault(x =>
x.UrlPage == urlPage &&
x.IdCategory == idCategory);
return Task.FromResult(helpPage);
});
//act
var result = await helpPageService.GetOrDefaultByUrlPageAndIdCategoryAsync(urlPage,
idCategory,
CancellationToken.None);
//assert
Assert.NotNull(result);
}
[Theory]
[InlineData(123)]
[InlineData(178)]
public async Task GetOrDefaultByIdAsync_ShouldReturn_HelpPageDto(int id)
{
//arrange
helpPageRepository.Setup(x => x.GetOrDefaultAsync(It.IsAny<int>(),
It.IsAny<CancellationToken>()))
.Returns(() =>
{
var helpPage = HelpPages.FirstOrDefault(x =>
x.Id == id);
return Task.FromResult(helpPage);
});
//act
var result = await helpPageService.GetOrDefaultByIdAsync(id,
CancellationToken.None);
//assert
Assert.NotNull(result);
}
}

View File

@ -0,0 +1,125 @@
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using Microsoft.AspNetCore.Authorization;
namespace AsbCloudWebApi.Controllers;
/// <summary>
/// Справки по страницам
/// </summary>
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class HelpPageController : ControllerBase
{
private readonly IHelpPageService helpPageService;
private readonly IUserRepository userRepository;
public HelpPageController(IHelpPageService helpPageService,
IUserRepository userRepository)
{
this.helpPageService = helpPageService;
this.userRepository = userRepository;
}
/// <summary>
/// Создание файла справки
/// </summary>
/// <param name="urlPage">Url страницы для которой предназначена эта справка</param>
/// <param name="idCategory">Id катагории файла</param>
/// <param name="file">Загружаемый файл</param>
/// <returns>Id созданной справки</returns>
[HttpPost]
[Permission]
[Route("saveFile")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> CreateAsync(string urlPage,
int idCategory,
[Required] IFormFile file)
{
int? idUser = User.GetUserId();
if(!idUser.HasValue)
return Forbid();
if (!userRepository.HasPermission(idUser.Value, $"HelpPage.create"))
return Forbid();
var helpPage = await helpPageService.GetOrDefaultByUrlPageAndIdCategoryAsync(urlPage,
idCategory,
CancellationToken.None);
using var fileStream = file.OpenReadStream();
if (helpPage is not null)
{
await helpPageService.UpdateAsync(helpPage,
idCategory,
file.FileName,
fileStream,
CancellationToken.None);
return Ok(helpPage.Id);
}
int helpPageId = await helpPageService.CreateAsync(urlPage,
idCategory,
file.FileName,
fileStream,
CancellationToken.None);
return Ok(helpPageId);
}
/// <summary>
/// Получение файла справки
/// </summary>
/// <param name="id">Id справки</param>
/// <returns>Файл</returns>
[HttpGet]
[Route("getById/{id}")]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetFileAsync(int id)
{
var helpPage = await helpPageService.GetOrDefaultByIdAsync(id,
CancellationToken.None);
if (helpPage is null)
return NotFound();
using var fileStream = helpPageService.GetFileStream(helpPage);
var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream,
CancellationToken.None);
memoryStream.Position = 0;
return File(memoryStream, "application/octet-stream", helpPage.Name);
}
/// <summary>
/// Получение информации о справке
/// </summary>
/// <param name="urlPage">Url страницы</param>
/// <param name="idCategory">Id категории</param>
/// <returns>Dto справки</returns>
[HttpGet]
[Route("getByUrlPage/{urlPage}/{idCategory}")]
[ProducesResponseType(typeof(HelpPageDto), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> GetByUrlPageAsync(string urlPage,
int idCategory)
{
var helpPage = await helpPageService.GetOrDefaultByUrlPageAndIdCategoryAsync(urlPage,
idCategory,
CancellationToken.None);
return Ok(helpPage);
}
}