DocumentService.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.IO.Abstractions;
  5. using System.Linq;
  6. using System.Security.Principal;
  7. using System.Threading.Tasks;
  8. using BO.AppServer.Business.Exceptions;
  9. using BO.AppServer.Business.Mapper;
  10. using BO.AppServer.Business.Services.Base;
  11. using BO.AppServer.Data.Entity;
  12. using BO.AppServer.Data.Repository;
  13. using BO.AppServer.Metadata.Dto;
  14. using BO.AppServer.Metadata.Enums;
  15. using Microsoft.AspNetCore.Identity;
  16. using Microsoft.Extensions.Logging;
  17. using Microsoft.Extensions.Options;
  18. using Quadarax.Foundation.Core.Data;
  19. using Quadarax.Foundation.Core.Data.Interface;
  20. using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;
  21. namespace BO.AppServer.Business.Services
  22. {
  23. public class DocumentService : UserContextService
  24. {
  25. #region *** Private fields ***
  26. private IFileSystem _fileSystem;
  27. private WorkspaceRepo _repoWorkspace;
  28. private MetadocumentRepo _repoDocument;
  29. private MimeTypeRepo _repoMType;
  30. private ArtifactRepo _repoArtifact;
  31. private StorageService _srvStorage;
  32. #endregion
  33. #region *** Constructors ***
  34. public DocumentService(
  35. WorkspaceRepo repoWorkspace,
  36. MetadocumentRepo repoDocument,
  37. MimeTypeRepo repoMType,
  38. ArtifactRepo repoArtifact,
  39. StorageService srvStorage,
  40. IFileSystem fileSystem,
  41. IOptions<Configuration.Configuration> config,
  42. UserRepo repoUser,
  43. UserManager<IdentityUser> userManager,
  44. IPrincipal currentPrincipal,
  45. ILoggerFactory logger) : base(config, repoUser, userManager, currentPrincipal, logger)
  46. {
  47. _repoWorkspace = repoWorkspace ?? throw new ArgumentNullException(nameof(repoWorkspace));
  48. _repoDocument = repoDocument ?? throw new ArgumentNullException(nameof(repoDocument));
  49. _repoMType = repoMType ?? throw new ArgumentNullException(nameof(repoMType));
  50. _repoArtifact = repoArtifact ?? throw new ArgumentNullException(nameof(repoArtifact));
  51. _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
  52. _srvStorage = srvStorage ?? throw new ArgumentNullException(nameof(srvStorage));
  53. }
  54. #endregion
  55. #region *** Public Operations ***
  56. public async Task<ResultValueDto<DocumentRDto>> GetDocumentAsync(long workspaceId, long documentId)
  57. {
  58. var document = CheckAndGetDocument(workspaceId, documentId);
  59. return new ResultValueDto<DocumentRDto>(document.Map<Metadocument, DocumentRDto>());
  60. }
  61. public async Task<ResultValueDto<DocumentRDto>> GetDocumentAsync(long workspaceId, IdentificationTypeEnum identificationType, string identificationValue)
  62. {
  63. var document = identificationType == IdentificationTypeEnum.Id ?
  64. _repoDocument.Get(ArgumentAsLong(nameof(identificationValue), identificationValue))
  65. : _repoDocument.GetByName(workspaceId, identificationValue);
  66. if (document == null)
  67. throw new NoDataException();
  68. return new ResultValueDto<DocumentRDto>(document.Map<Metadocument, DocumentRDto>());
  69. }
  70. public async Task<ResultsValueDto<DocumentRDto>> GetAllDocumentsAsync(long workspaceId, DocumentsScopeEnums scope, PagingDto paging)
  71. {
  72. CheckAndGetWorkspace(workspaceId);
  73. IList<Metadocument> documents = new List<Metadocument>();
  74. switch (scope)
  75. {
  76. case DocumentsScopeEnums.All:
  77. documents = _repoDocument.Query(paging).Where(x => x.Structure.WorkspaceId == workspaceId).OrderBy(x => x.Name).ToList();
  78. break;
  79. case DocumentsScopeEnums.Pending:
  80. documents = _repoDocument.Query(paging).Where(x => x.Structure.WorkspaceId == workspaceId && x.Status == (int)DocumentStatusEnum.Pending).OrderBy(x => x.Name).ToList();
  81. break;
  82. case DocumentsScopeEnums.Done:
  83. documents = _repoDocument.Query(paging).Where(x => x.Structure.WorkspaceId == workspaceId && x.Status == (int)DocumentStatusEnum.DoneOk).OrderBy(x => x.Name).ToList();
  84. break;
  85. case DocumentsScopeEnums.Working:
  86. documents = _repoDocument.Query(paging).Where(x => x.Structure.WorkspaceId == workspaceId && x.Status == (int)DocumentStatusEnum.Processing).OrderBy(x => x.Name).ToList();
  87. break;
  88. case DocumentsScopeEnums.Failed:
  89. documents = _repoDocument.Query(paging).Where(x => x.Structure.WorkspaceId == workspaceId && x.Status == (int)DocumentStatusEnum.DoneFail).OrderBy(x => x.Name).ToList();
  90. break;
  91. }
  92. return new ResultsValueDto<DocumentRDto>(documents.MapList<Metadocument, DocumentRDto>());
  93. }
  94. public async Task<ResultValueDto<DocumentRDto>> GetDocumentByNameAsync(long workspaceId, string name)
  95. {
  96. CheckAndGetWorkspace(workspaceId);
  97. IList<Metadocument> documents = new List<Metadocument>();
  98. var document = _repoDocument.GetByName(workspaceId, name);
  99. if (document == null)
  100. throw new NoDataException();
  101. return new ResultValueDto<DocumentRDto>(document.Map<Metadocument, DocumentRDto>());
  102. }
  103. public async Task<ResultsValueDto<DocumentRDto>> GetAllDocumentsAsync(DocumentsScopeEnums scope, PagingDto paging)
  104. {
  105. IList<Metadocument> documents = new List<Metadocument>();
  106. switch (scope)
  107. {
  108. case DocumentsScopeEnums.All:
  109. documents = _repoDocument.Query(paging).OrderBy(x => x.Name).ToList();
  110. break;
  111. case DocumentsScopeEnums.Pending:
  112. documents = _repoDocument.Query(paging).Where(x => x.Status == (int)DocumentStatusEnum.Pending).OrderBy(x => x.Name).ToList();
  113. break;
  114. case DocumentsScopeEnums.Done:
  115. documents = _repoDocument.Query(paging).Where(x => x.Status == (int)DocumentStatusEnum.DoneOk).OrderBy(x => x.Name).ToList();
  116. break;
  117. case DocumentsScopeEnums.Working:
  118. documents = _repoDocument.Query(paging).Where(x => x.Status == (int)DocumentStatusEnum.Processing).OrderBy(x => x.Name).ToList();
  119. break;
  120. case DocumentsScopeEnums.Failed:
  121. documents = _repoDocument.Query(paging).Where(x => x.Status == (int)DocumentStatusEnum.DoneFail).OrderBy(x => x.Name).ToList();
  122. break;
  123. }
  124. return new ResultsValueDto<DocumentRDto>(documents.MapList<Metadocument, DocumentRDto>());
  125. }
  126. public async Task<ResultValueDto<DocumentRDto>> CreateDocumentAsync(long workspaceId, DocumentCDto document)
  127. {
  128. var workspace = CheckAndGetWorkspace(workspaceId);
  129. var exists = _repoDocument.Query(new Paging())
  130. .Any(x => x.Structure.WorkspaceId == workspaceId && x.Name == document.Name);
  131. if (exists)
  132. {
  133. var exc = new EntityAlreadyExistsException(typeof(Metadocument), document.Name);
  134. Log.LogWarning(exc.Message);
  135. throw exc;
  136. }
  137. var newDocument = _repoDocument.New();
  138. document.CopyToDto(newDocument);
  139. newDocument.Created = DateTime.Now;
  140. newDocument.CreatedByUser = GetCurrentUser();
  141. newDocument.Status = (int)DocumentStatusEnum.New;
  142. newDocument.StructureId = workspace.Structures.First().Id;
  143. _repoDocument.Set(newDocument);
  144. _repoDocument.Commit();
  145. return new ResultValueDto<DocumentRDto>(newDocument.Map<Metadocument, DocumentRDto>());
  146. }
  147. public async Task<ResultsValueDto<ArtifactRDto>> ClearDocumentArtifactsAsync(long workspaceId, long documentId, bool force)
  148. {
  149. var document = CheckAndGetDocument(workspaceId, documentId);
  150. CheckDocumentStatusForUpdate(document, force);
  151. var artifacts = _repoArtifact.GetArtifacts(documentId).ToArray(); // must be to array
  152. foreach (var artifact in artifacts)
  153. {
  154. await _srvStorage.DeleteContent(artifact.Id);
  155. _repoArtifact.Remove(artifact.Id);
  156. Log.LogInformation($"Document '{document.Name}' ({documentId}) artifact '{artifact.Name + artifact.Extension}' ({artifact.Id}) removed.");
  157. }
  158. document.Modified = DateTime.Now;
  159. document.ModifiedByUser = GetCurrentUser();
  160. _repoDocument.Set(document);
  161. _repoArtifact.Commit();
  162. _repoDocument.Commit();
  163. return new ResultsValueDto<ArtifactRDto>(artifacts.MapList<Artifact, ArtifactRDto>());
  164. }
  165. public async Task<ResultValueDto<ArtifactRDto>> AppendArtifactAsync(long workspaceId, long documentId,
  166. ArtifactCDto artifact,bool replace, ArtifactTypeEnum type, Stream content)
  167. {
  168. //TODO: Rewrite better way - mainly rollback scenario
  169. var document = CheckAndGetDocument(workspaceId, documentId);
  170. var existingArtifact = _repoArtifact.GetArtifactByName(documentId, artifact.Name, artifact.Extension);
  171. if (existingArtifact!=null && !replace)
  172. {
  173. var exc = new EntityAlreadyExistsException(typeof(Artifact), artifact.Name + "." + artifact.Extension);
  174. Log.LogWarning(exc.Message);
  175. throw exc;
  176. }
  177. var mtype = _repoMType.GetByMimeType(artifact.MimeType);
  178. if (mtype == null)
  179. {
  180. var exc = new EntityNotExistsException(typeof(MimeType), artifact.MimeType, 0);
  181. Log.LogWarning(exc.Message);
  182. throw exc;
  183. }
  184. var newArtifact = (existingArtifact != null && replace) ? existingArtifact : _repoArtifact.New();
  185. newArtifact.Name = artifact.Name;
  186. newArtifact.Extension = artifact.Extension;
  187. newArtifact.Length = content.Length;
  188. newArtifact.Metadocument = document;
  189. newArtifact.MimeType = mtype;
  190. newArtifact.Created = DateTime.Now;
  191. newArtifact.CreatedByUser = GetCurrentUser();
  192. newArtifact.Type = (int)type;
  193. _repoArtifact.Set(newArtifact);
  194. document = _repoDocument.Get(documentId);
  195. document.Modified = DateTime.Now;
  196. document.ModifiedByUser = GetCurrentUser();
  197. _repoDocument.Set(document);
  198. _repoArtifact.Commit();
  199. _repoDocument.Commit();
  200. await _srvStorage.PushContentAsync(newArtifact.Id, content);
  201. return new ResultValueDto<ArtifactRDto>(newArtifact.Map<Artifact, ArtifactRDto>());
  202. }
  203. public async Task<Tuple<Stream, string>> GetArtifactAsync(long workspaceId, long documentId, long artifactId)
  204. {
  205. var artifact = CheckAndGetArtifact(workspaceId, documentId, artifactId);
  206. var result = await _srvStorage.PopContentAsync(artifact.Id);
  207. return new Tuple<Stream, string>(result.Item1, artifact.MimeType.Mimetype1);
  208. }
  209. public ArtifactCDto CreateArtifactDto(string fileName, string contentType)
  210. {
  211. var result = new ArtifactCDto();
  212. result.Extension = _fileSystem.Path.GetExtension(fileName);
  213. result.MimeType = contentType;
  214. result.Name = _fileSystem.Path.GetFileNameWithoutExtension(fileName);
  215. return result;
  216. }
  217. #endregion
  218. #region *** Private Operations ***
  219. private Workspace CheckAndGetWorkspace(long workspaceId)
  220. {
  221. var workspace = _repoWorkspace.Get(workspaceId);
  222. if (workspace == null)
  223. {
  224. var exc = new EntityNotExistsException(typeof(Workspace), nameof(Workspace.Id), workspaceId);
  225. Log.LogWarning(exc.Message);
  226. throw exc;
  227. }
  228. return workspace;
  229. }
  230. private Metadocument CheckAndGetDocument(long workspaceId, long documentId)
  231. {
  232. CheckAndGetWorkspace(workspaceId);
  233. var document = _repoDocument.Get(workspaceId ,documentId);
  234. if (document == null)
  235. {
  236. var exc = new EntityNotExistsException(typeof(Metadocument), nameof(Metadocument.Id), documentId);
  237. Log.LogWarning(exc.Message);
  238. throw exc;
  239. }
  240. return document;
  241. }
  242. private void CheckDocumentStatusForUpdate(Metadocument document, bool force)
  243. {
  244. if (document == null)
  245. throw new ArgumentNullException(nameof(document));
  246. var currentStatus = (DocumentStatusEnum)document.Status;
  247. var allowedStatus = new DocumentStatusEnum[]
  248. {
  249. DocumentStatusEnum.New,
  250. DocumentStatusEnum.Pending,
  251. };
  252. if (!allowedStatus.Contains(currentStatus))
  253. {
  254. var message = $"Document '{document.Name}' [{document.Id}] has not allowed status '{currentStatus}' for operation UPDATE. Allowed statuses are {string.Join(",", allowedStatus)}.";
  255. Log.LogWarning(message + (force ? " Skip due Force flag." : string.Empty));
  256. if (!force)
  257. throw new InvalidDocumentStateException(document.Name, document.Id, currentStatus, "update");
  258. }
  259. }
  260. private Artifact CheckAndGetArtifact(long workspaceId, long documentId, long artifactId)
  261. {
  262. CheckAndGetWorkspace(workspaceId);
  263. CheckAndGetDocument(workspaceId, documentId);
  264. var artifact = _repoArtifact.GetArtifact(documentId, artifactId);
  265. if (artifact == null)
  266. {
  267. var exc = new EntityNotExistsException(typeof(Artifact), nameof(Artifact.Id), artifactId);
  268. Log.LogWarning(exc.Message);
  269. throw exc;
  270. }
  271. return artifact;
  272. }
  273. #endregion
  274. }
  275. }