DocumentService.cs 14 KB

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