From 03e50a429711c26aa1e4a3d657264ca346c7a170 Mon Sep 17 00:00:00 2001 From: "ai.astrakhantsev" Date: Wed, 5 Oct 2022 15:47:02 +0500 Subject: [PATCH 1/3] #6539681 test file service --- AsbCloudWebApi.Tests/IRepositoryFactory.cs | 17 ++ AsbCloudWebApi.Tests/RepositoryFactory.cs | 44 ++++ .../ServicesTests/FileServiceTest.cs | 222 ++++++++++++++++++ AsbCloudWebApi.Tests/TestHelpter.cs | 3 +- 4 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 AsbCloudWebApi.Tests/IRepositoryFactory.cs create mode 100644 AsbCloudWebApi.Tests/RepositoryFactory.cs create mode 100644 AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs diff --git a/AsbCloudWebApi.Tests/IRepositoryFactory.cs b/AsbCloudWebApi.Tests/IRepositoryFactory.cs new file mode 100644 index 00000000..e7028689 --- /dev/null +++ b/AsbCloudWebApi.Tests/IRepositoryFactory.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudWebApi.Tests +{ + public interface IRepositoryFactory + { + Task DeleteAsync(int id, CancellationToken token); + Task> GetAllAsync(CancellationToken token); + TDto? GetOrDefault(int id); + Task GetOrDefaultAsync(int id, CancellationToken token); + Task InsertAsync(TDto newItem, CancellationToken token); + Task InsertRangeAsync(IEnumerable newItems, CancellationToken token); + Task UpdateAsync(TDto item, CancellationToken token); + } +} \ No newline at end of file diff --git a/AsbCloudWebApi.Tests/RepositoryFactory.cs b/AsbCloudWebApi.Tests/RepositoryFactory.cs new file mode 100644 index 00000000..8495751c --- /dev/null +++ b/AsbCloudWebApi.Tests/RepositoryFactory.cs @@ -0,0 +1,44 @@ +using AsbCloudApp.Services; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudWebApi.Tests +{ + public class RepositoryFactory + { + public static Mock Make(IList data) + where TDto : AsbCloudApp.Data.IId + where TRepository : class, ICrudService + { + var repositoryMock = new Mock(); + + repositoryMock.Setup(x => x.InsertAsync(It.IsAny(), It.IsAny())) + .Returns((TDto dto, CancellationToken token) => { + var id = data.Max(x => x.Id); + dto.Id = ++id; + data.Add(dto); + return Task.FromResult(dto.Id); + }); + repositoryMock.Setup(x => x.DeleteAsync(It.IsAny(), It.IsAny())) + .Returns((int idFile, CancellationToken token) => { + var cnt = data.Count; + var dto = data.FirstOrDefault(x => x.Id == idFile); + data.Remove(dto); + return Task.FromResult(cnt - data.Count); + }); + + repositoryMock.Setup(x => x.GetAllAsync(It.IsAny())).ReturnsAsync(data); + repositoryMock.Setup(x => x.GetOrDefaultAsync(It.IsAny(), It.IsAny())) + .Returns((int idFile, CancellationToken token) => { + return Task.FromResult(data.FirstOrDefault(x => x.Id == idFile)); + }); + + return repositoryMock; + } + } +} diff --git a/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs new file mode 100644 index 00000000..bf3c6442 --- /dev/null +++ b/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs @@ -0,0 +1,222 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Services; +using AsbCloudDb.Model; +using AsbCloudInfrastructure.Services; +using DocumentFormat.OpenXml.Spreadsheet; +using DocumentFormat.OpenXml.Wordprocessing; +using Moq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using static AsbCloudWebApi.Tests.TestHelpter; + +namespace AsbCloudWebApi.Tests.ServicesTests +{ + public class FileServiceTest + { + private IFileService fileService; + + private static UserDto Author = new UserDto { + Id = 1, + IdCompany = 1 + }; + + private static List FileMarks = new List { + new FileMarkDto + { + Id = 132, + IdFile = 1742, + User = Author, + Comment = "qqq", + IdMarkType = 1, + DateCreated = DateTime.Now, + IsDeleted = false + }, + new FileMarkDto + { + Id = 133, + IdFile = 1742, + User = Author, + Comment = "qqq3", + IdMarkType = 1, + DateCreated = DateTime.Now, + IsDeleted = false + } + }; + + private static List Files = new List { + new FileInfoDto { + Id = 1742, + IdAuthor = 1, + Author = Author, + IdWell = 90, + IdCategory = 10040, + Name = "test.txt", + Size = 0, + UploadDate = DateTime.Now, + FileMarks = FileMarks + }, + new FileInfoDto + { + Id = 1743, + IdAuthor = 1, + Author = Author, + IdWell = 90, + IdCategory = 10021, + Name = "test1.txt", + Size = 0, + UploadDate = DateTime.Now + } + }; + + public FileServiceTest() + { + var repositoryMock = RepositoryFactory.Make(Files); + + repositoryMock.Setup(x => x.GetByMarkId(It.IsAny(), It.IsAny())) + .Returns((int idMark, CancellationToken token) => { + var data = Files.FirstOrDefault(x => x.FileMarks.Any(m => m.Id == idMark)); + return Task.FromResult(data); + }); + repositoryMock.Setup(x => x.GetInfoByIdsAsync(It.IsAny>(), It.IsAny())) + .Returns((int[] idsFile, CancellationToken token) => { + var data = Files.Where(x => idsFile.Contains(x.Id)); + return Task.FromResult(data); + }); + + repositoryMock.Setup(x => x.DeleteAsync(It.IsAny>(), It.IsAny())) + .Returns((int[] idsFile, CancellationToken token) => { + var dtos = Files.Where(x => idsFile.Contains(x.Id)).ToArray(); + Files.RemoveAll(x => dtos.Select(d => d.Id).Contains(x.Id)); + return Task.FromResult(dtos.AsEnumerable()); + }); + + repositoryMock.Setup(x => x.MarkFileMarkAsDeletedAsync(It.IsAny>(), It.IsAny())) + .Returns((int[] idsMarks, CancellationToken token) => { + var data = FileMarks.Where(m => idsMarks.Contains(m.Id)); + + foreach (var fileMark in data) + fileMark.IsDeleted = true; + + var result = data.All(x => x.IsDeleted) ? 1 : 0; + return Task.FromResult(result); + }); + + repositoryMock.Setup(x => x.MarkAsDeletedAsync(It.IsAny(), It.IsAny())) + .Returns((int idFile, CancellationToken token) => { + var result = Files.Where(x => x.Id == idFile).Any() ? 1 : 0; + return Task.FromResult(result); + }); + + repositoryMock.Setup(x => x.GetInfosByWellIdAsync(It.IsAny(), It.IsAny())) + .Returns((int idWell, CancellationToken token) => { + var data = Files.Where(x => x.IdWell == idWell); + return Task.FromResult(data); + }); + + repositoryMock.Setup(x => x.GetInfosByCategoryAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((int idWell, int idCategory, CancellationToken token) => { + var data = Files.Where(x => x.IdWell == idWell && x.IdCategory == idCategory); + return Task.FromResult(data); + }); + + repositoryMock.Setup(x => x.CreateFileMarkAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((FileMarkDto dto, int idUser, CancellationToken token) => { + dto.Id = FileMarks.Max(x => x.Id) + 1; + dto.DateCreated = DateTime.UtcNow; + dto.User = null; + FileMarks.Add(dto); + + var result = FileMarks.Any(x => x.Id == dto.Id) ? 1 : 0; + return Task.FromResult(result); + }); + + fileService = new FileService(repositoryMock.Object); + } + + [Fact] + public async Task GetByMarkId_returns_FileInfo_by_idMark() + { + var data = await fileService.GetByMarkId(133, CancellationToken.None); + Assert.NotNull(data); + } + + [Fact] + public async Task GetInfoAsync_throws_FileNotFoundException() + { + await Assert.ThrowsAsync(async () => + { + await fileService.GetInfoAsync(1743, CancellationToken.None); + }); + } + + [Fact] + public async Task GetInfoByIdsAsync_throws_FileNotFoundException() + { + await Assert.ThrowsAsync(async () => + { + await fileService.GetInfoByIdsAsync(new int[] { 1742, 1743 }, CancellationToken.None); + }); + } + + [Fact] + public async Task SaveAsync_returns_FileInfoa() + { + var stream = new FileStream("D:\\test\\test.txt", FileMode.Open); + var data = await fileService.SaveAsync(90, 1, 10040, "test.txt", stream, CancellationToken.None); + Assert.NotNull(data); + } + + [Fact] + public async Task DeleteAsync() + { + var result = await fileService.DeleteAsync(new int[] { 1743 }, CancellationToken.None); + Assert.True(result > 0); + } + + [Fact] + public async Task MarkFileMarkAsDeletedAsync() + { + var result = await fileService.MarkFileMarkAsDeletedAsync(new int[] { 132, 133 }, CancellationToken.None); + Assert.True(result > 0); + } + + [Fact] + public async Task MarkAsDeletedAsync() + { + var result = await fileService.MarkAsDeletedAsync(1742, CancellationToken.None); + Assert.True(result > 0); + } + + [Fact] + public async Task GetInfosByWellIdAsync_returns_FileInfo() + { + var data = await fileService.GetInfosByWellIdAsync(90, CancellationToken.None); + Assert.NotNull(data); + } + + [Fact] + public async Task GetInfosByCategoryAsync_returns_FileInfo() + { + var data = await fileService.GetInfosByCategoryAsync(90, 10040, CancellationToken.None); + Assert.NotNull(data); + } + + [Fact] + public async Task CreateFileMarkAsync() + { + var dto = new FileMarkDto { + Comment = "test", + IdFile = 1742, + IdMarkType = 1 + }; + var result = await fileService.CreateFileMarkAsync(dto, 1, CancellationToken.None); + Assert.True(result > 0); + } + } +} diff --git a/AsbCloudWebApi.Tests/TestHelpter.cs b/AsbCloudWebApi.Tests/TestHelpter.cs index 9322503b..f7c8e8a9 100644 --- a/AsbCloudWebApi.Tests/TestHelpter.cs +++ b/AsbCloudWebApi.Tests/TestHelpter.cs @@ -1,4 +1,5 @@ -using AsbCloudDb.Model; +using AsbCloudApp.Services; +using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; From b6ce4cad7712bd4091f1829692c0d6d83629840f Mon Sep 17 00:00:00 2001 From: "ai.astrakhantsev" Date: Thu, 6 Oct 2022 13:41:46 +0500 Subject: [PATCH 2/3] #6539681 file storage repository + tests --- AsbCloudApp/AsbCloudApp.csproj | 8 +++ AsbCloudApp/Services/IFileService.cs | 5 -- .../Services/IFileStorageRepository.cs | 51 ++++++++++++++++ AsbCloudInfrastructure/DependencyInjection.cs | 1 + .../Repository/FileStorageRepository.cs | 59 +++++++++++++++++++ .../DrillingProgram/DrillingProgramService.cs | 3 +- .../Services/FileService.cs | 41 ++++--------- .../Services/ReportService.cs | 3 +- AsbCloudWebApi.Tests/RepositoryFactory.cs | 4 +- .../ServicesTests/FileServiceTest.cs | 25 ++++---- 10 files changed, 150 insertions(+), 50 deletions(-) create mode 100644 AsbCloudApp/Services/IFileStorageRepository.cs create mode 100644 AsbCloudInfrastructure/Repository/FileStorageRepository.cs diff --git a/AsbCloudApp/AsbCloudApp.csproj b/AsbCloudApp/AsbCloudApp.csproj index 64a7b66a..5c826cb1 100644 --- a/AsbCloudApp/AsbCloudApp.csproj +++ b/AsbCloudApp/AsbCloudApp.csproj @@ -13,4 +13,12 @@ + + + + + + + + diff --git a/AsbCloudApp/Services/IFileService.cs b/AsbCloudApp/Services/IFileService.cs index a83d6d5c..a87397f7 100644 --- a/AsbCloudApp/Services/IFileService.cs +++ b/AsbCloudApp/Services/IFileService.cs @@ -14,11 +14,6 @@ namespace AsbCloudApp.Services /// public interface IFileService { - /// - /// Директория хранения файлов - /// - string RootPath { get; } - /// /// Сохранить файл /// diff --git a/AsbCloudApp/Services/IFileStorageRepository.cs b/AsbCloudApp/Services/IFileStorageRepository.cs new file mode 100644 index 00000000..6659e976 --- /dev/null +++ b/AsbCloudApp/Services/IFileStorageRepository.cs @@ -0,0 +1,51 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudApp.Services +{ + /// + /// Репозиторий хранения фалов + /// + public interface IFileStorageRepository + { + /// + /// Директория хранения файлов + /// + string RootPath { get; } + + /// + /// Получение длинны фала и проверка его наличия, если отсутствует падает исключение + /// + /// + /// + long GetLengthFile(string srcFilePath); + + /// + /// Перемещение файла + /// + /// + /// + void MoveFile (string srcFilePath, string filePath); + + /// + /// Копирование файла + /// + /// + Task CopyFileAsync(string filePath, Stream fileStream, CancellationToken token); + + /// + /// Удаление файла + /// + /// + void DeleteFile(string fileName); + + /// + /// Проверка наличия файла + /// + /// + /// + /// + bool FileExists(string fullPath, string fileName); + } +} diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index f178b648..5c1bf56f 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -155,6 +155,7 @@ namespace AsbCloudInfrastructure .Include(c => c.Wells) .Include(c => c.Deposit))); // может быть включен в сервис ClusterService services.AddTransient(); + services.AddTransient(); // Subsystem service services.AddTransient, CrudCacheServiceBase>(); services.AddTransient(); diff --git a/AsbCloudInfrastructure/Repository/FileStorageRepository.cs b/AsbCloudInfrastructure/Repository/FileStorageRepository.cs new file mode 100644 index 00000000..6cb38cfb --- /dev/null +++ b/AsbCloudInfrastructure/Repository/FileStorageRepository.cs @@ -0,0 +1,59 @@ +using AsbCloudApp.Exceptions; +using AsbCloudApp.Services; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Repository +{ + public class FileStorageRepository : IFileStorageRepository + { + public string RootPath { get; private set; } + + public FileStorageRepository() + { + RootPath = "files"; + } + + public async Task CopyFileAsync(string filePath, Stream fileStream, CancellationToken token) + { + CreateDirectory(filePath); + using var newfileStream = new FileStream(filePath, FileMode.Create); + await fileStream.CopyToAsync(newfileStream, token).ConfigureAwait(false); + } + + public void DeleteFile(string fileName) + { + if (File.Exists(fileName)) + File.Delete(fileName); + } + + public long GetLengthFile(string srcFilePath) + { + if (!File.Exists(srcFilePath)) + throw new ArgumentInvalidException($"file {srcFilePath} doesn't exist", nameof(srcFilePath)); + + var sysFileInfo = new FileInfo(srcFilePath); + return sysFileInfo.Length; + } + + public void MoveFile(string srcFilePath, string filePath) + { + CreateDirectory(filePath); + File.Move(srcFilePath, filePath); + } + + public bool FileExists(string fullPath, string fileName) + { + if (!File.Exists(fullPath)) + throw new FileNotFoundException("not found", fileName); + + return true; + } + + private void CreateDirectory(string filePath) + { + Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + } + } +} diff --git a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs index 0e840524..2780e7b1 100644 --- a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs +++ b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs @@ -481,7 +481,8 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram .Options; using var context = new AsbCloudDbContext(contextOptions); var fileRepository = new FileRepository(context); - var fileService = new FileService(fileRepository); + var fileStorageRepository = new FileStorageRepository(); + var fileService = new FileService(fileRepository, fileStorageRepository); var files = state.Parts.Select(p => fileService.GetUrl(p.File)); DrillingProgramMaker.UniteExcelFiles(files, tempResultFilePath, state.Parts, well); await fileService.MoveAsync(idWell, null, idFileCategoryDrillingProgram, resultFileName, tempResultFilePath, token); diff --git a/AsbCloudInfrastructure/Services/FileService.cs b/AsbCloudInfrastructure/Services/FileService.cs index cdfed260..5cb60fcd 100644 --- a/AsbCloudInfrastructure/Services/FileService.cs +++ b/AsbCloudInfrastructure/Services/FileService.cs @@ -16,13 +16,13 @@ namespace AsbCloudInfrastructure.Services { public class FileService : IFileService { - public string RootPath { get; private set; } private readonly IFileRepository fileRepository; + private readonly IFileStorageRepository fileStorageRepository; - public FileService(IFileRepository fileRepository) + public FileService(IFileRepository fileRepository, IFileStorageRepository fileStorageRepository) { - RootPath = "files"; this.fileRepository = fileRepository; + this.fileStorageRepository = fileStorageRepository; } public async Task MoveAsync(int idWell, int? idUser, int idCategory, @@ -30,10 +30,7 @@ namespace AsbCloudInfrastructure.Services { destinationFileName = Path.GetFileName(destinationFileName); srcFilePath = Path.GetFullPath(srcFilePath); - if (!File.Exists(srcFilePath)) - throw new ArgumentInvalidException($"file {srcFilePath} doesn't exist", nameof(srcFilePath)); - - var sysFileInfo = new System.IO.FileInfo(srcFilePath); + var fileSize = fileStorageRepository.GetLengthFile(srcFilePath); //save info to db var dto = new FileInfoDto { @@ -41,14 +38,13 @@ namespace AsbCloudInfrastructure.Services IdAuthor = idUser, IdCategory = idCategory, Name = destinationFileName, - Size = sysFileInfo.Length + Size = fileSize }; var fileId = await fileRepository.InsertAsync(dto, token) .ConfigureAwait(false); string filePath = MakeFilePath(idWell, idCategory, destinationFileName, fileId); - Directory.CreateDirectory(Path.GetDirectoryName(filePath)); - File.Move(srcFilePath, filePath); + fileStorageRepository.MoveFile(srcFilePath, filePath); return await GetInfoAsync(fileId, token); } @@ -71,18 +67,14 @@ namespace AsbCloudInfrastructure.Services //save stream to disk string filePath = MakeFilePath(idWell, idCategory, fileFullName, fileId); - - Directory.CreateDirectory(Path.GetDirectoryName(filePath)); - - using var newfileStream = new FileStream(filePath, FileMode.Create); - await fileStream.CopyToAsync(newfileStream, token).ConfigureAwait(false); + await fileStorageRepository.CopyFileAsync(filePath, fileStream, token); return await GetInfoAsync(fileId, token); } private string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId) { - return Path.Combine(RootPath, $"{idWell}", + return Path.Combine(fileStorageRepository.RootPath, $"{idWell}", $"{idCategory}", $"{fileId}" + $"{Path.GetExtension(fileFullName)}"); } @@ -95,10 +87,7 @@ namespace AsbCloudInfrastructure.Services var relativePath = GetUrl(dto.IdWell, dto.IdCategory, dto.Id, ext); var fullPath = Path.GetFullPath(relativePath); - if (!File.Exists(fullPath)) - { - throw new FileNotFoundException("not found", relativePath); - } + fileStorageRepository.FileExists(fullPath, relativePath); return dto; } @@ -119,8 +108,7 @@ namespace AsbCloudInfrastructure.Services foreach (var file in files) { var fileName = GetUrl(file.IdWell, file.IdCategory, file.Id, Path.GetExtension(file.Name)); - if (File.Exists(fileName)) - File.Delete(fileName); + fileStorageRepository.DeleteFile(fileName); } return files.Any() ? 1 : 0; @@ -137,7 +125,7 @@ namespace AsbCloudInfrastructure.Services GetUrl(fileInfo.IdWell, fileInfo.IdCategory, fileInfo.Id, Path.GetExtension(fileInfo.Name)); public string GetUrl(int idWell, int idCategory, int idFile, string dotExtention) => - Path.Combine(RootPath, idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}"); + Path.Combine(fileStorageRepository.RootPath, idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}"); public Task MarkFileMarkAsDeletedAsync(int idMark, CancellationToken token) @@ -153,12 +141,9 @@ namespace AsbCloudInfrastructure.Services var ext = Path.GetExtension(entity.Name); var relativePath = GetUrl(entity.IdWell, entity.IdCategory, entity.Id, ext); var fullPath = Path.GetFullPath(relativePath); - if (!File.Exists(fullPath)) - { - throw new FileNotFoundException("not found", relativePath); - } + fileStorageRepository.FileExists(fullPath, relativePath); } - + return result; } diff --git a/AsbCloudInfrastructure/Services/ReportService.cs b/AsbCloudInfrastructure/Services/ReportService.cs index 53d8653a..7d60f852 100644 --- a/AsbCloudInfrastructure/Services/ReportService.cs +++ b/AsbCloudInfrastructure/Services/ReportService.cs @@ -67,7 +67,8 @@ namespace AsbCloudInfrastructure.Services generator.Make(reportFileName); var fileRepository = new FileRepository(context); - var fileService = new FileService(fileRepository); + var fileStorageRepository = new FileStorageRepository(); + var fileService = new FileService(fileRepository, fileStorageRepository); var fileInfo = await fileService.MoveAsync(idWell, idUser, ReportCategoryId, reportFileName, reportFileName, token); progressHandler.Invoke(new diff --git a/AsbCloudWebApi.Tests/RepositoryFactory.cs b/AsbCloudWebApi.Tests/RepositoryFactory.cs index 8495751c..a8588dda 100644 --- a/AsbCloudWebApi.Tests/RepositoryFactory.cs +++ b/AsbCloudWebApi.Tests/RepositoryFactory.cs @@ -1,9 +1,7 @@ using AsbCloudApp.Services; using Moq; -using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -11,7 +9,7 @@ namespace AsbCloudWebApi.Tests { public class RepositoryFactory { - public static Mock Make(IList data) + public static Mock Make(ICollection data) where TDto : AsbCloudApp.Data.IId where TRepository : class, ICrudService { diff --git a/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs index bf3c6442..828f19ae 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs @@ -1,6 +1,7 @@ using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; +using AsbCloudInfrastructure.Repository; using AsbCloudInfrastructure.Services; using DocumentFormat.OpenXml.Spreadsheet; using DocumentFormat.OpenXml.Wordprocessing; @@ -136,7 +137,11 @@ namespace AsbCloudWebApi.Tests.ServicesTests return Task.FromResult(result); }); - fileService = new FileService(repositoryMock.Object); + var storageRepositoryMock = new Mock(); + + storageRepositoryMock.Setup(x => x.RootPath).Returns("files"); + + fileService = new FileService(repositoryMock.Object, storageRepositoryMock.Object); } [Fact] @@ -147,27 +152,23 @@ namespace AsbCloudWebApi.Tests.ServicesTests } [Fact] - public async Task GetInfoAsync_throws_FileNotFoundException() + public async Task GetInfoAsync_returns_FileInfo() { - await Assert.ThrowsAsync(async () => - { - await fileService.GetInfoAsync(1743, CancellationToken.None); - }); + var data = await fileService.GetInfoAsync(1742, CancellationToken.None); + Assert.NotNull(data); } [Fact] - public async Task GetInfoByIdsAsync_throws_FileNotFoundException() + public async Task GetInfoByIdsAsync_returns_FileInfo() { - await Assert.ThrowsAsync(async () => - { - await fileService.GetInfoByIdsAsync(new int[] { 1742, 1743 }, CancellationToken.None); - }); + var data = await fileService.GetInfoByIdsAsync(new int[] { 1742, 1743 }, CancellationToken.None); + Assert.NotNull(data); } [Fact] public async Task SaveAsync_returns_FileInfoa() { - var stream = new FileStream("D:\\test\\test.txt", FileMode.Open); + using var stream = new MemoryStream(); var data = await fileService.SaveAsync(90, 1, 10040, "test.txt", stream, CancellationToken.None); Assert.NotNull(data); } From fe49c90e58d064e06a53aafdd206b40fbba4fc68 Mon Sep 17 00:00:00 2001 From: "ai.astrakhantsev" Date: Thu, 6 Oct 2022 14:37:03 +0500 Subject: [PATCH 3/3] #6539681 file service refactor --- AsbCloudApp/AsbCloudApp.csproj | 4 + .../IFileRepository.cs | 3 +- .../IFileStorageRepository.cs | 4 +- .../Services/FileService.cs | 156 +++++++++++++-- AsbCloudApp/Services/IFileService.cs | 181 ------------------ AsbCloudInfrastructure/DependencyInjection.cs | 3 +- .../Repository/FileRepository.cs | 2 +- .../Repository/FileStorageRepository.cs | 2 +- .../DrillingProgram/DrillingProgramService.cs | 4 +- .../Services/WellFinalDocumentsService.cs | 4 +- .../DrillingProgramServiceTest.cs | 4 +- .../ServicesTests/FileServiceTest.cs | 7 +- .../WellFinalDocumentsServiceTest.cs | 4 +- AsbCloudWebApi/Controllers/FileController.cs | 4 +- .../Controllers/ReportController.cs | 4 +- 15 files changed, 170 insertions(+), 216 deletions(-) rename AsbCloudApp/{Services => Repositories}/IFileRepository.cs (98%) rename AsbCloudApp/{Services => Repositories}/IFileStorageRepository.cs (94%) rename {AsbCloudInfrastructure => AsbCloudApp}/Services/FileService.cs (59%) delete mode 100644 AsbCloudApp/Services/IFileService.cs diff --git a/AsbCloudApp/AsbCloudApp.csproj b/AsbCloudApp/AsbCloudApp.csproj index 5c826cb1..7a10181c 100644 --- a/AsbCloudApp/AsbCloudApp.csproj +++ b/AsbCloudApp/AsbCloudApp.csproj @@ -21,4 +21,8 @@ + + + + diff --git a/AsbCloudApp/Services/IFileRepository.cs b/AsbCloudApp/Repositories/IFileRepository.cs similarity index 98% rename from AsbCloudApp/Services/IFileRepository.cs rename to AsbCloudApp/Repositories/IFileRepository.cs index e01891cc..e36d1001 100644 --- a/AsbCloudApp/Services/IFileRepository.cs +++ b/AsbCloudApp/Repositories/IFileRepository.cs @@ -1,10 +1,11 @@ using AsbCloudApp.Data; +using AsbCloudApp.Services; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -namespace AsbCloudApp.Services +namespace AsbCloudApp.Repositories { /// /// Сервис доступа к файлам diff --git a/AsbCloudApp/Services/IFileStorageRepository.cs b/AsbCloudApp/Repositories/IFileStorageRepository.cs similarity index 94% rename from AsbCloudApp/Services/IFileStorageRepository.cs rename to AsbCloudApp/Repositories/IFileStorageRepository.cs index 6659e976..9d821861 100644 --- a/AsbCloudApp/Services/IFileStorageRepository.cs +++ b/AsbCloudApp/Repositories/IFileStorageRepository.cs @@ -2,7 +2,7 @@ using System.Threading; using System.Threading.Tasks; -namespace AsbCloudApp.Services +namespace AsbCloudApp.Repositories { /// /// Репозиторий хранения фалов @@ -26,7 +26,7 @@ namespace AsbCloudApp.Services /// /// /// - void MoveFile (string srcFilePath, string filePath); + void MoveFile(string srcFilePath, string filePath); /// /// Копирование файла diff --git a/AsbCloudInfrastructure/Services/FileService.cs b/AsbCloudApp/Services/FileService.cs similarity index 59% rename from AsbCloudInfrastructure/Services/FileService.cs rename to AsbCloudApp/Services/FileService.cs index 5cb60fcd..b27c4383 100644 --- a/AsbCloudInfrastructure/Services/FileService.cs +++ b/AsbCloudApp/Services/FileService.cs @@ -1,10 +1,5 @@ using AsbCloudApp.Data; -using AsbCloudApp.Exceptions; -using AsbCloudApp.Services; -using AsbCloudDb.Model; -using AsbCloudInfrastructure.Repository; -using Mapster; -using Microsoft.EntityFrameworkCore; +using AsbCloudApp.Repositories; using System; using System.Collections.Generic; using System.IO; @@ -12,19 +7,37 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -namespace AsbCloudInfrastructure.Services +namespace AsbCloudApp.Services { - public class FileService : IFileService + /// + /// Сервис доступа к файлам + /// + public class FileService { private readonly IFileRepository fileRepository; private readonly IFileStorageRepository fileStorageRepository; + /// + /// Сервис доступа к файлам + /// + /// + /// public FileService(IFileRepository fileRepository, IFileStorageRepository fileStorageRepository) { this.fileRepository = fileRepository; this.fileStorageRepository = fileStorageRepository; } + /// + /// переместить файл + /// + /// + /// + /// + /// + /// + /// + /// public async Task MoveAsync(int idWell, int? idUser, int idCategory, string destinationFileName, string srcFilePath, CancellationToken token = default) { @@ -49,6 +62,16 @@ namespace AsbCloudInfrastructure.Services return await GetInfoAsync(fileId, token); } + /// + /// Сохранить файл + /// + /// + /// + /// + /// + /// + /// + /// public async Task SaveAsync(int idWell, int? idUser, int idCategory, string fileFullName, Stream fileStream, CancellationToken token) { @@ -72,12 +95,12 @@ namespace AsbCloudInfrastructure.Services return await GetInfoAsync(fileId, token); } - private string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId) - { - return Path.Combine(fileStorageRepository.RootPath, $"{idWell}", - $"{idCategory}", $"{fileId}" + $"{Path.GetExtension(fileFullName)}"); - } - + /// + /// Инфо о файле + /// + /// + /// + /// public async Task GetInfoAsync(int idFile, CancellationToken token) { @@ -92,9 +115,21 @@ namespace AsbCloudInfrastructure.Services return dto; } + /// + /// удалить файл + /// + /// + /// + /// public Task DeleteAsync(int idFile, CancellationToken token) => DeleteAsync(new int[] { idFile }, token); + /// + /// удалить файлы + /// + /// + /// + /// public async Task DeleteAsync(IEnumerable ids, CancellationToken token) { if (ids is null || !ids.Any()) @@ -114,6 +149,11 @@ namespace AsbCloudInfrastructure.Services return files.Any() ? 1 : 0; } + /// + /// получить путь для скачивания + /// + /// + /// public async Task GetUrl(int idFile) { var fileInfo = await fileRepository.GetOrDefaultAsync(idFile, CancellationToken.None).ConfigureAwait(false); @@ -121,16 +161,41 @@ namespace AsbCloudInfrastructure.Services return GetUrl(fileInfo.IdWell, fileInfo.IdCategory, fileInfo.Id, Path.GetExtension(fileInfo.Name)); } + /// + /// получить путь для скачивания + /// + /// + /// public string GetUrl(FileInfoDto fileInfo) => GetUrl(fileInfo.IdWell, fileInfo.IdCategory, fileInfo.Id, Path.GetExtension(fileInfo.Name)); + /// + /// получить путь для скачивания + /// + /// + /// + /// + /// + /// public string GetUrl(int idWell, int idCategory, int idFile, string dotExtention) => Path.Combine(fileStorageRepository.RootPath, idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}"); + /// + /// пометить метку файла как удаленную + /// + /// + /// + /// public Task MarkFileMarkAsDeletedAsync(int idMark, CancellationToken token) => fileRepository.MarkFileMarkAsDeletedAsync(new int[] { idMark }, token); + /// + /// Инфо о файле + /// + /// + /// + /// public async Task> GetInfoByIdsAsync(IEnumerable idsFile, CancellationToken token) { var result = await fileRepository.GetInfoByIdsAsync(idsFile, token).ConfigureAwait(false); @@ -147,38 +212,101 @@ namespace AsbCloudInfrastructure.Services return result; } + /// + /// Получить список файлов в контейнере + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// public async Task> GetInfosAsync(int idWell, int idCategory, string companyName = default, string fileName = default, DateTime begin = default, DateTime end = default, int skip = 0, int take = 32, CancellationToken token = default) => await fileRepository.GetInfosAsync(idWell, idCategory, companyName, fileName, begin, end, skip, take, token) .ConfigureAwait(false); + /// + /// Пометить файл как удаленный + /// + /// + /// + /// public async Task MarkAsDeletedAsync(int idFile, CancellationToken token = default) => await fileRepository.MarkAsDeletedAsync(idFile, token) .ConfigureAwait(false); + /// + /// добавить метку на файл + /// + /// + /// + /// + /// public async Task CreateFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token) => await fileRepository.CreateFileMarkAsync(fileMarkDto, idUser, token) .ConfigureAwait(false); + /// + /// Получить запись по id + /// + /// + /// + /// public async Task GetOrDefaultAsync(int id, CancellationToken token) => await fileRepository.GetOrDefaultAsync(id, token) .ConfigureAwait(false); + /// + /// получить инфо о файле по метке + /// + /// + /// + /// public async Task GetByMarkId(int idMark, CancellationToken token) => await fileRepository.GetByMarkId(idMark, token) .ConfigureAwait(false); + /// + /// получить инфо о файле по метке + /// + /// + /// + /// public async Task MarkFileMarkAsDeletedAsync(IEnumerable idsMarks, CancellationToken token) => await fileRepository.MarkFileMarkAsDeletedAsync(idsMarks, token) .ConfigureAwait(false); + /// + /// Получение файлов по скважине + /// + /// + /// + /// public async Task> GetInfosByWellIdAsync(int idWell, CancellationToken token) => await fileRepository.GetInfosByWellIdAsync(idWell, token) .ConfigureAwait(false); + /// + /// Получить файлы определенной категории + /// + /// + /// + /// + /// public async Task> GetInfosByCategoryAsync(int idWell, int idCategory, CancellationToken token) => await fileRepository.GetInfosByCategoryAsync(idWell, idCategory, token) .ConfigureAwait(false); + + private string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId) + { + return Path.Combine(fileStorageRepository.RootPath, $"{idWell}", + $"{idCategory}", $"{fileId}" + $"{Path.GetExtension(fileFullName)}"); + } } } diff --git a/AsbCloudApp/Services/IFileService.cs b/AsbCloudApp/Services/IFileService.cs deleted file mode 100644 index a87397f7..00000000 --- a/AsbCloudApp/Services/IFileService.cs +++ /dev/null @@ -1,181 +0,0 @@ -using AsbCloudApp.Data; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudApp.Services -{ - //TODO: refactor IFileService - - /// - /// Сервис доступа к файлам - /// - public interface IFileService - { - /// - /// Сохранить файл - /// - /// - /// - /// - /// - /// - /// - /// - Task SaveAsync(int idWell, int? idUser, int idCategory, string fileFullName, Stream fileStream, CancellationToken token = default); - - /// - /// Инфо о файле - /// - /// - /// - /// - Task GetInfoAsync(int idFile, - CancellationToken token); - - /// - /// удалить файл - /// - /// - /// - /// - Task DeleteAsync(int id, CancellationToken token); - - /// - /// удалить файлы - /// - /// - /// - /// - Task DeleteAsync(IEnumerable ids, CancellationToken token); - - /// - /// получить путь для скачивания - /// - /// - /// - string GetUrl(FileInfoDto fileInfo); - - /// - /// получить путь для скачивания - /// - /// - /// - Task GetUrl(int idFile); - - /// - /// получить путь для скачивания - /// - /// - /// - /// - /// - /// - string GetUrl(int idWell, int idCategory, int idFile, string dotExtention); - - /// - /// пометить метку файла как удаленную - /// - /// - /// - /// - Task MarkFileMarkAsDeletedAsync(int idMark, CancellationToken token); - - /// - /// переместить файл - /// - /// - /// - /// - /// - /// - /// - /// - Task MoveAsync(int idWell, int? idUser, int idCategory, string destinationFileName, string srcFileFullName, CancellationToken token = default); - - /// - /// Инфо о файле - /// - /// - /// - /// - Task> GetInfoByIdsAsync(IEnumerable idsFile, CancellationToken token); - - /// - /// Получить список файлов в контейнере - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - Task> GetInfosAsync(int idWell, - int idCategory, string companyName = default, string fileName = default, DateTime begin = default, - DateTime end = default, int skip = 0, int take = 32, CancellationToken token = default); - - /// - /// Пометить файл как удаленный - /// - /// - /// - /// - Task MarkAsDeletedAsync(int idFile, CancellationToken token = default); - - /// - /// добавить метку на файл - /// - /// - /// - /// - /// - Task CreateFileMarkAsync(FileMarkDto fileMarkDto, int idUser, CancellationToken token); - - /// - /// Получить запись по id - /// - /// - /// - /// - Task GetOrDefaultAsync(int id, CancellationToken token); - - /// - /// получить инфо о файле по метке - /// - /// - /// - /// - Task GetByMarkId(int idMark, CancellationToken token); - - /// - /// пометить метки файлов как удаленные - /// - /// - /// - /// - Task MarkFileMarkAsDeletedAsync(IEnumerable idsMarks, CancellationToken token); - - /// - /// Получение файлов по скважине - /// - /// - /// - /// - Task> GetInfosByWellIdAsync(int idWell, CancellationToken token); - - /// - /// Получить файлы определенной категории - /// - /// - /// - /// - /// - Task> GetInfosByCategoryAsync(int idWell, int idCategory, CancellationToken token); - } -} diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 5c1bf56f..ab3ba9e2 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -1,6 +1,7 @@ using AsbCloudApp.Data; using AsbCloudApp.Data.SAUB; using AsbCloudApp.Data.Subsystems; +using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudApp.Services.Subsystems; using AsbCloudDb.Model; @@ -105,7 +106,7 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/AsbCloudInfrastructure/Repository/FileRepository.cs b/AsbCloudInfrastructure/Repository/FileRepository.cs index 2726c2b7..0cca5b8b 100644 --- a/AsbCloudInfrastructure/Repository/FileRepository.cs +++ b/AsbCloudInfrastructure/Repository/FileRepository.cs @@ -1,5 +1,5 @@ using AsbCloudApp.Data; -using AsbCloudApp.Services; +using AsbCloudApp.Repositories; using AsbCloudDb.Model; using Mapster; using Microsoft.EntityFrameworkCore; diff --git a/AsbCloudInfrastructure/Repository/FileStorageRepository.cs b/AsbCloudInfrastructure/Repository/FileStorageRepository.cs index 6cb38cfb..7466f3a3 100644 --- a/AsbCloudInfrastructure/Repository/FileStorageRepository.cs +++ b/AsbCloudInfrastructure/Repository/FileStorageRepository.cs @@ -1,5 +1,5 @@ using AsbCloudApp.Exceptions; -using AsbCloudApp.Services; +using AsbCloudApp.Repositories; using System.IO; using System.Threading; using System.Threading.Tasks; diff --git a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs index 2780e7b1..ca0827b0 100644 --- a/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs +++ b/AsbCloudInfrastructure/Services/DrillingProgram/DrillingProgramService.cs @@ -20,7 +20,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram private static readonly Dictionary drillingProgramCreateErrors = new Dictionary(); private readonly IAsbCloudDbContext context; - private readonly IFileService fileService; + private readonly FileService fileService; private readonly IUserService userService; private readonly IWellService wellService; private readonly IConfiguration configuration; @@ -50,7 +50,7 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram public DrillingProgramService( IAsbCloudDbContext context, - IFileService fileService, + FileService fileService, IUserService userService, IWellService wellService, IConfiguration configuration, diff --git a/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs b/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs index 4d5f6427..62d82522 100644 --- a/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs +++ b/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs @@ -22,7 +22,7 @@ namespace AsbCloudInfrastructure.Services public class WellFinalDocumentsService : IWellFinalDocumentsService { private readonly IAsbCloudDbContext context; - private readonly IFileService fileService; + private readonly FileService fileService; private readonly IUserService userService; private readonly IWellService wellService; private readonly IConfiguration configuration; @@ -32,7 +32,7 @@ namespace AsbCloudInfrastructure.Services private const int FileServiceThrewException = -1; public WellFinalDocumentsService(IAsbCloudDbContext context, - IFileService fileService, + FileService fileService, IUserService userService, IWellService wellService, IConfiguration configuration, diff --git a/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs index 949c8c46..fa72df35 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DrillingProgramServiceTest.cs @@ -78,7 +78,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests new RelationCompanyWell { IdCompany = 3003, IdWell = 3002, }, }; - private readonly Mock fileServiceMock; + private readonly Mock fileServiceMock; private readonly Mock userServiceMock; private readonly Mock wellServiceMock; private readonly Mock configurationMock; @@ -97,7 +97,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests db.RelationCompaniesWells.AddRange(relationsCompanyWell); db.SaveChanges(); - fileServiceMock = new Mock(); + fileServiceMock = new Mock(); userServiceMock = new Mock(); wellServiceMock = new Mock(); configurationMock = new Mock(); diff --git a/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs index 828f19ae..025cc7bb 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/FileServiceTest.cs @@ -1,4 +1,5 @@ using AsbCloudApp.Data; +using AsbCloudApp.Repositories; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Repository; @@ -20,7 +21,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests { public class FileServiceTest { - private IFileService fileService; + private FileService fileService; private static UserDto Author = new UserDto { Id = 1, @@ -166,9 +167,9 @@ namespace AsbCloudWebApi.Tests.ServicesTests } [Fact] - public async Task SaveAsync_returns_FileInfoa() + public async Task SaveAsync_returns_FileInfo() { - using var stream = new MemoryStream(); + using var stream = new MemoryStream(Array.Empty()); var data = await fileService.SaveAsync(90, 1, 10040, "test.txt", stream, CancellationToken.None); Assert.NotNull(data); } diff --git a/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs index ac53a5b9..1718b92d 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/WellFinalDocumentsServiceTest.cs @@ -17,7 +17,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests { private readonly AsbCloudDbContext context; private WellFinalDocumentsService service; - private readonly Mock fileServiceMock; + private readonly Mock fileServiceMock; private readonly Mock userServiceMock; private readonly Mock wellServiceMock; private readonly Mock configurationMock; @@ -44,7 +44,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests context = TestHelpter.MakeTestContext(); context.SaveChanges(); - fileServiceMock = new Mock(); + fileServiceMock = new Mock(); userServiceMock = new Mock(); userServiceMock.Setup(x => x.GetAllAsync(CancellationToken.None)).Returns(Task.Run(() => users.Select(x => (UserExtendedDto)x))); diff --git a/AsbCloudWebApi/Controllers/FileController.cs b/AsbCloudWebApi/Controllers/FileController.cs index a7711247..378b5a50 100644 --- a/AsbCloudWebApi/Controllers/FileController.cs +++ b/AsbCloudWebApi/Controllers/FileController.cs @@ -20,10 +20,10 @@ namespace AsbCloudWebApi.Controllers [Authorize] public class FileController : ControllerBase { - private readonly IFileService fileService; + private readonly FileService fileService; private readonly IWellService wellService; - public FileController(IFileService fileService, IWellService wellService) + public FileController(FileService fileService, IWellService wellService) { this.fileService = fileService; this.wellService = wellService; diff --git a/AsbCloudWebApi/Controllers/ReportController.cs b/AsbCloudWebApi/Controllers/ReportController.cs index 03897582..d3df1092 100644 --- a/AsbCloudWebApi/Controllers/ReportController.cs +++ b/AsbCloudWebApi/Controllers/ReportController.cs @@ -18,12 +18,12 @@ namespace AsbCloudWebApi.Controllers public class ReportController : ControllerBase { private readonly IReportService reportService; - private readonly IFileService fileService; + private readonly FileService fileService; private readonly IWellService wellService; private readonly IHubContext reportsHubContext; public ReportController(IReportService reportService, IWellService wellService, - IFileService fileService, IHubContext reportsHubContext) + FileService fileService, IHubContext reportsHubContext) { this.reportService = reportService; this.fileService = fileService;