ソースを参照

document-add, document-clear implemented

Dalibor Votruba 4 年 前
コミット
7af80f82a3

+ 4 - 1
AppServer/Business/ErrorCodes.txt

@@ -22,4 +22,7 @@ Business.StorageService (1300 - 1399)
 1300	Content for artifact '{0}' [{1}] not found!
 
 Business.WorkspaceService (1400 - 1499)
-1400	Workspace with APIKey '{0}' not exists or API password is not valid!
+1400	Workspace with APIKey '{0}' not exists or API password is not valid!
+
+Business.DocumentService (1500 - 1599)
+1500	Invalid document '{0}' [{1}] state '{2}' for operation '{3}'. Operation cancelled.

+ 20 - 0
AppServer/Business/Exceptions/InvalidDocumentStateException.cs

@@ -0,0 +1,20 @@
+using System;
+using BO.AppServer.Metadata.Enums;
+using Quadarax.Foundation.Core.Exceptions;
+
+namespace BO.AppServer.Business.Exceptions
+{
+    public class InvalidDocumentStateException : CodeException
+    {
+        private const string CS_MESSAGE = "Invalid document '{0}' [{1}] state '{2}' for operation '{3}'. Operation cancelled.";
+        private const int CN_CODE = 1500;
+
+        public InvalidDocumentStateException(string name,long id, DocumentStatusEnum status,string operation, Exception innerException) : base(CN_CODE, string.Format(CS_MESSAGE,  name, id, status, operation), innerException)
+        {
+        }
+
+        public InvalidDocumentStateException(string name,long id, DocumentStatusEnum status,string operation) : base(CN_CODE, string.Format(CS_MESSAGE,  name, id, status, operation))
+        {
+        }
+    }
+}

+ 30 - 4
AppServer/Business/Mapper/MapperLong.cs

@@ -46,11 +46,37 @@ namespace BO.AppServer.Business.Mapper
             Define<WorkspaceUDto, Workspace>();
 
 
-            DefineBiDirection<Metadocument, DocumentRDto>();
-            DefineBiDirection<Metadocument, DocumentCDto>();
+            Define<DocumentCDto, Metadocument>();
+            Define<Metadocument, DocumentRDto>(builder =>
+            {
+                builder.Custom(dest => dest.Audit, src => new BoAuditDto()
+                {
+                    Created = src.Created,
+                    Changed = src.Modified,
+                    CreatedBy = src.CreatedByUser.Name,
+                    ChangedBy = src.ModifiedByUser != null ? src.ModifiedByUser.Name : string.Empty
+                });
+                //calculate on demand  builder.Custom(dest => dest.BillingPlanCode, src => src.WorkspaceBillings.OrderByDescending(x=>x.Created).FirstOrDefault().BillingPlan.Code);
+            });
             
-            DefineBiDirection<Artifact, ArtifactRDto>();
-            DefineBiDirection<Artifact, ArtifactCDto>();
+            Define<ArtifactCDto, Artifact>();
+            Define<Artifact, ArtifactRDto>(builder =>
+            {
+                builder.Custom(dest => dest.Audit, src => new BoAuditDto()
+                {
+                    Created = src.Created,
+                    Changed = null,
+                    CreatedBy = src.CreatedByUser.Name,
+                    ChangedBy = string.Empty
+                });
+                //calculate on demand  builder.Custom(dest => dest.BillingPlanCode, src => src.WorkspaceBillings.OrderByDescending(x=>x.Created).FirstOrDefault().BillingPlan.Code);
+            });
+
+
+
+
+
+
 
             Define<Structure, StructureRDto>(builder =>
             {

+ 56 - 8
AppServer/Business/Services/DocumentService.cs

@@ -153,16 +153,41 @@ namespace BO.AppServer.Business.Services
             return new ResultValueDto<DocumentRDto>(newDocument.Map<Metadocument, DocumentRDto>());
         }
 
+        public async Task<ResultsValueDto<ArtifactRDto>> ClearDocumentArtifactsAsync(long workspaceId, long documentId, bool force)
+        {
+        
+            var document = CheckAndGetDocument(workspaceId, documentId);
+            CheckDocumentStatusForUpdate(document, force);
+
+
+            var artifacts = _repoArtifact.GetArtifacts(documentId).ToArray(); // must be to array
+            foreach (var artifact in artifacts)
+            {
+                await _srvStorage.DeleteContent(artifact.Id);
+                _repoArtifact.Remove(artifact.Id);
+                Log.LogInformation($"Document '{document.Name}' ({documentId}) artifact '{artifact.Name + artifact.Extension}' ({artifact.Id}) removed.");
+            }
+            
+            document.Modified = DateTime.Now;
+            document.ModifiedByUser = GetCurrentUser();
+            _repoDocument.Set(document);
+            _repoArtifact.Commit();
+            _repoDocument.Commit();
+
+            return new ResultsValueDto<ArtifactRDto>(artifacts.MapList<Artifact, ArtifactRDto>());
+
+        }
+
+
 
         public async Task<ResultValueDto<ArtifactRDto>> AppendArtifactAsync(long workspaceId, long documentId,
-            ArtifactCDto artifact, ArtifactTypeEnum type, Stream content)
+            ArtifactCDto artifact,bool replace, ArtifactTypeEnum type, Stream content)
         {
             //TODO: Rewrite better way - mainly rollback scenario
 
             var document = CheckAndGetDocument(workspaceId, documentId);
-            var exists = document.Artifacts.Any(x =>
-                x.Name.ToLower() == artifact.Name.ToLower() && x.Extension.ToLower() == artifact.Extension.ToLower());
-            if (exists)
+            var existingArtifact = _repoArtifact.GetArtifactByName(documentId, artifact.Name, artifact.Extension);
+            if (existingArtifact!=null && !replace)
             {
                 var exc = new EntityAlreadyExistsException(typeof(Artifact), artifact.Name + "." + artifact.Extension);
                 Log.LogWarning(exc.Message);
@@ -177,8 +202,8 @@ namespace BO.AppServer.Business.Services
                 throw exc;
             }
 
-            var newArtifact = _repoArtifact.New();
-
+            var newArtifact = (existingArtifact != null && replace) ? existingArtifact : _repoArtifact.New();
+            
             newArtifact.Name = artifact.Name;
             newArtifact.Extension = artifact.Extension;
             newArtifact.Length = content.Length;
@@ -199,10 +224,10 @@ namespace BO.AppServer.Business.Services
 
             return new ResultValueDto<ArtifactRDto>(newArtifact.Map<Artifact, ArtifactRDto>());
 
-
         }
 
 
+
         public async Task<Tuple<Stream, string>> GetArtifactAsync(long workspaceId, long documentId, long artifactId)
         {
             var artifact = CheckAndGetArtifact(workspaceId, documentId, artifactId);
@@ -253,11 +278,34 @@ namespace BO.AppServer.Business.Services
             return document;
         }
 
+        private void CheckDocumentStatusForUpdate(Metadocument document, bool force)
+        {
+            if (document == null)
+                throw new ArgumentNullException(nameof(document));
+
+            var currentStatus = (DocumentStatusEnum)document.Status;
+            var allowedStatus = new DocumentStatusEnum[]
+            {
+                DocumentStatusEnum.New,
+                DocumentStatusEnum.Pending,
+            };
+
+            if (!allowedStatus.Contains(currentStatus))
+            {
+                var message = $"Document '{document.Name}' [{document.Id}] has not allowed status '{currentStatus}' for operation UPDATE. Allowed statuses are {string.Join(",", allowedStatus)}.";
+                Log.LogWarning(message +  (force ? " Skip due Force flag." : string.Empty));
+                if (!force)
+                    throw new InvalidDocumentStateException(document.Name, document.Id, currentStatus, "update");
+            }
+            
+        }
+        
+
         private Artifact CheckAndGetArtifact(long workspaceId, long documentId, long artifactId)
         {
             CheckAndGetWorkspace(workspaceId);
             CheckAndGetDocument(workspaceId, documentId);
-            var artifact = _repoDocument.GetArtifact(documentId, artifactId);
+            var artifact = _repoArtifact.GetArtifact(documentId, artifactId);
             if (artifact == null)
             {
                 var exc = new EntityNotExistsException(typeof(Artifact), nameof(Artifact.Id), artifactId);

+ 12 - 3
AppServer/Connector.Console/Connection.cs

@@ -173,14 +173,23 @@ namespace BO.Connector.Console
             return result.Value;
         }
 
-        public async Task<ArtifactRDto> AppendWorkspaceDocumentArtifactAsync(string workspaceApiKey,string apiKeyPassword,long documentId, string fileName,string mimeType, Stream contentStream, string userNameContext)
+        public async Task<ArtifactRDto> AppendWorkspaceDocumentArtifactAsync(string workspaceApiKey,string apiKeyPassword,long documentId, string fileName,string mimeType,bool overwriteIfExists, Stream contentStream, string userNameContext)
         {
             CheckIsOpen();
-            var header = GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword));
+            var header = GetOverwriteHeaderAttributes(overwriteIfExists,GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword)));
             var result = await CallPostStreamMultipartAsync<ResultValueDto<ArtifactRDto>>($"{Ticket}/workspace/{workspaceApiKey}/document/{documentId}/artifact/input", fileName, contentStream,mimeType, header);
             return result.Value;
         }
-         
+        
+        public async Task<IEnumerable<ArtifactRDto>> ClearWorkspaceDocumentArtifactAsync(string workspaceApiKey,string apiKeyPassword,long documentId,bool force, string userNameContext)
+        {
+            CheckIsOpen();
+            var header = GetForceHeaderAttributes(force,GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword)));
+            var result = await CallRequestAsync<ResultsValueDto<ArtifactRDto>>($"{Ticket}/workspace/{workspaceApiKey}/document/{documentId}/artifact",RestCallTypeEnum.Delete,null, header);
+            return result.Values;
+        }
+
+
         #endregion
 
 

+ 30 - 1
AppServer/Connector/AbstractConnection.cs

@@ -152,6 +152,24 @@ namespace BO.Connector
             return source;
         }
 
+        protected IDictionary<string, string> GetOverwriteHeaderAttributes(bool overwrite, IDictionary<string,string> source = null)
+        {
+            if (source == null)
+                source = new Dictionary<string, string>();
+
+            source.Add(new KeyValuePair<string, string>("overwrite", overwrite.ToString()));
+            return source;
+        }
+
+        protected IDictionary<string, string> GetForceHeaderAttributes(bool force, IDictionary<string,string> source = null)
+        {
+            if (source == null)
+                source = new Dictionary<string, string>();
+
+            source.Add(new KeyValuePair<string, string>("force", force.ToString()));
+            return source;
+        }
+
         protected IDictionary<string, string> GetUserContextHeaderAttributes(string userNameContext, IDictionary<string,string> source = null)
         {
             if (source == null)
@@ -302,7 +320,18 @@ namespace BO.Connector
                     resp = await _client.PutAsync(relativeUrlApiCall, req);
                     break;
                 case RestCallTypeEnum.Delete:
-                    resp = await _client.DeleteAsync(relativeUrlApiCall);
+                    using (var delRq = new HttpRequestMessage(HttpMethod.Delete, relativeUrlApiCall))
+                    {
+                        if (headerAttributes != null)
+                            foreach (var key in headerAttributes.Keys)
+                            {
+                                delRq.Headers.Remove(key);
+                                delRq.Headers.Add(key, headerAttributes[key]);
+                            }
+
+                        resp = await _client.SendAsync(delRq);
+                    }
+
                     break;
                 default:
                     throw new NotSupportedException($"{method} REST API operation is not implemented.");

+ 21 - 1
AppServer/Data/Repository/ArtifactRepo.cs

@@ -1,4 +1,7 @@
-using BO.AppServer.Data.Entity;
+using System.Collections.Generic;
+using System.Linq;
+using BO.AppServer.Data.Entity;
+using Quadarax.Foundation.Core.Data;
 using Quadarax.Foundation.Core.Data.Domain;
 using Quadarax.Foundation.Core.Data.Interface.Domain;
 using Quadarax.Foundation.Core.Data.Repository;
@@ -10,5 +13,22 @@ namespace BO.AppServer.Data.Repository
         public ArtifactRepo(IUnitOfWork<DataDomain> unitOfWork) : base(unitOfWork)
         {
         }
+
+        public Artifact GetArtifact(long metadocumentId, long artifactId)
+        {
+            return Query(new Paging()).FirstOrDefault(x => x.MetadocumentId == metadocumentId && x.Id== artifactId);
+        }
+
+        public IEnumerable<Artifact> GetArtifacts(long metadocumentId)
+        {
+            return Query(new Paging()).Where(x => x.MetadocumentId == metadocumentId);
+        }
+
+
+        public Artifact GetArtifactByName(long metadocumentId, string nameWithoutExtension, string extension)
+        {
+            return Query(new Paging()).FirstOrDefault(x => x.Id == metadocumentId && x.Name.ToLower() == nameWithoutExtension.ToLower() && x.Extension.ToLower() == extension.ToLower());
+        }
+
     }
 }

+ 1 - 5
AppServer/Data/Repository/MetadocumentRepo.cs

@@ -1,5 +1,6 @@
 using System.Linq;
 using BO.AppServer.Data.Entity;
+using Microsoft.EntityFrameworkCore;
 using Quadarax.Foundation.Core.Data;
 using Quadarax.Foundation.Core.Data.Domain;
 using Quadarax.Foundation.Core.Data.Interface.Domain;
@@ -28,9 +29,4 @@ public class MetadocumentRepo : EntityRepository<long,  Metadocument>
 
         }
 
-        public Artifact GetArtifact(long metadocumentId, long artifactId)
-        {
-            return Query(new Paging()).Where(x => x.Id == metadocumentId)
-                .SelectMany(x => x.Artifacts).FirstOrDefault(x => x.Id == artifactId);
-        }
     }

+ 12 - 3
AppServer/Web/Services/ConsoleController.cs

@@ -106,7 +106,6 @@ namespace BO.AppServer.Web.Services
         #endregion
 
         #region *** Statistics ***
-
         [HttpGet("{ticket}/versions")]
         public async Task<ResultsValueDto<AssemblyInfoDto>> GetVersions(string ticket)
         {
@@ -231,7 +230,7 @@ namespace BO.AppServer.Web.Services
         [RequestFormLimits(MultipartBodyLengthLimit = 209715200)]
         [RequestSizeLimit(209715200)]
         //  [FromBody] ArtifactCDto artifact, 
-        public async Task<ResultValueDto<ArtifactRDto>> AppendWorkspaceDocumentArtifact(string ticket, string wrkspApiKey, long documentId,[FromHeader] string apiKeyPassword, [FromHeader] string context, IFormFile content)
+        public async Task<ResultValueDto<ArtifactRDto>> AppendWorkspaceDocumentArtifact(string ticket, string wrkspApiKey, long documentId,[FromHeader] string apiKeyPassword, [FromHeader] string context, [FromHeader] bool overwrite,  IFormFile content)
         {
             CheckAccess(ticket);
             return await Call(async () =>
@@ -239,10 +238,20 @@ namespace BO.AppServer.Web.Services
                 var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
                 var artifact = _srvDocument.CreateArtifactDto(content.FileName, content.ContentType);
                 await using var stream = content.OpenReadStream();
-                return await _srvDocument.AppendArtifactAsync(id, documentId, artifact, ArtifactTypeEnum.Input, stream);
+                return await _srvDocument.AppendArtifactAsync(id, documentId, artifact, overwrite, ArtifactTypeEnum.Input, stream);
             });
         }
 
+        [HttpDelete("{Ticket}/workspace/{wrkspApiKey}/document/{documentId}/artifact")]
+        public async Task<ResultsValueDto<ArtifactRDto>> DeleteWorkspaceDocumentArtifacts(string ticket, string wrkspApiKey, long documentId,[FromHeader] string apiKeyPassword, [FromHeader] string context, [FromHeader] bool force)
+        {
+            CheckAccess(ticket);
+            return await Call(async () =>
+            {
+                var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
+                return await _srvDocument.ClearDocumentArtifactsAsync(id, documentId,force);
+            });
+        }
 
         #endregion
     }

+ 7 - 8
Console/Commands/Document/DocumentAddCommand.cs

@@ -80,25 +80,24 @@ namespace BO.Console.Commands.Document
             }
             // 3. Append artifacts
             WriteInfo($"Appending {ArtifactFiles.Count} artifacts...");
-            //var appendArtTasks = new List<Task>();
+            var appendArtTasks = new List<Task>();
             foreach (var artFile in ArtifactFiles)
             {
-                //appendArtTasks.Add(Task.Factory.StartNew(async () =>
-                //{
+                appendArtTasks.Add(Task.Factory.StartNew(async () =>
+                {
                     var dtStart = DateTime.Now;
                     ArtifactRDto art;
-                    //await using (var fs = FileSystem.File.OpenRead(artFile))
-                    using (var fs = FileSystem.File.OpenRead(artFile))
+                    await using (var fs = FileSystem.File.OpenRead(artFile))
                     {
                         art = Client.AppendWorkspaceDocumentArtifactAsync(ApiKey, ApiKeyPassword, document.Id,
-                            FileSystem.Path.GetFileName(artFile), MimeTypeUtil.GetMimeType(FileSystem.Path.GetExtension(artFile)), fs, UserName).Result;
+                            FileSystem.Path.GetFileName(artFile), MimeTypeUtil.GetMimeType(FileSystem.Path.GetExtension(artFile)),IsForce.GetValueOrDefault(), fs, UserName).Result;
                     }
                     WriteInfo($"Artifact '{art.Name}' [{art.Id}] appended to document '{document.Name}' as mime '{art.MimeType}' @ {(DateTime.Now - dtStart).ToReadableString()}");
-                //}));
+                }));
             }
 
             // 4. Sync all
-            //Task.WaitAll(appendArtTasks.ToArray());
+            Task.WaitAll(appendArtTasks.ToArray());
             return new Result();
         }
         #endregion

+ 96 - 0
Console/Commands/Document/DocumentClearCommand.cs

@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using BO.Console.Commands.Base;
+using Quadarax.Foundation.Core.QConsole;
+using Quadarax.Foundation.Core.QConsole.Argument;
+using Quadarax.Foundation.Core.QConsole.Attributes;
+using Quadarax.Foundation.Core.QConsole.Value;
+using Quadarax.Foundation.Core.Value;
+
+namespace BO.Console.Commands.Document
+{
+    [CommandDefinition]
+    internal class DocumentClearCommand: AbstractWorkspaceCommand 
+    {
+        #region *** Constants ***
+        private const string CS_CMD_DOCCLR_NAME = "document_clear";
+        private const string CS_CMD_DOCCLR_DESCR = "Add new metadocument with artifact to workspace or append artifact to existing document.";
+
+
+        private const string CS_ARG_DESC_USER = "User name context. If not specified use default user context (who is logged in console).";
+
+        private const string CS_ARG_NAME_NAME = "name";
+        private const string CS_ARG_HINT_NAME = "<document_name>";
+        private const string CS_ARG_DESC_NAME = "Metadata document name. If not specified use first artifact name. If document already exists, appends artifact to existing metadocument.";
+
+        private const string CS_ARG_DESC_FORCE = "Flag if set, then clears artifacts and ignores metadocument state, otherwise cancel operation.";
+
+        #endregion
+
+        #region *** Properties ***
+
+        public override string Name => CS_CMD_DOCCLR_NAME;
+        public override string Description => CS_CMD_DOCCLR_DESCR;
+
+
+        protected string DocumentName { get; set; }
+
+        #endregion
+
+        #region *** Constructor ***
+        public DocumentClearCommand(Engine engine) : base(engine)
+        {
+        }
+        #endregion
+
+        #region *** EXECUTE ***
+        protected override Result OnExecute()
+        {
+            WriteDebugInfo($"Getting metadocument '{DocumentName}' in workspace...");
+            var document = Client.GetWorkspaceDocumentByNameAsync(ApiKey, ApiKeyPassword, DocumentName, UserName).Result;
+            if (document!=null)
+            {
+                WriteDebugInfo($"Document '{DocumentName}' exists [{document.Id}] in workspace. Deleting artifacts...");
+                var deletedArtifacts = Client.ClearWorkspaceDocumentArtifactAsync(ApiKey, ApiKeyPassword, document.Id,
+                    IsForce.GetValueOrDefault(), UserName).Result;
+
+                int cnt = 0;
+                foreach (var art in deletedArtifacts)
+                {
+                    WriteInfo($"Artifact '{art.Name + art.Extension}' [{art.Id}] (Created:{art.Audit.Created}) DELETED.");
+                    cnt++;
+                }
+                WriteCaption($"Deleted {cnt} artifacts.");
+            }
+            else
+                return new Result(new Exception($"Document '{DocumentName}' not found."));
+
+            return new Result();
+        }
+        #endregion
+
+        #region *** Protected overrides ***
+        protected override void OnValidateArguments()
+        {
+            base.OnValidateArguments();
+            DocumentName = GetArgumentValueOrDefault<string>(CS_ARG_NAME_NAME);
+
+        }
+
+        #endregion
+
+        #region *** Private operations ***
+
+        protected override IEnumerable<AbstractArgument> OnSetupArguments()
+        {
+            var args = base.OnSetupArguments().ToList();
+            args.Add(new NamedArgument(CS_ARG_NAME_NAME, CS_ARG_DESC_NAME,CS_ARG_HINT_NAME, TypeValuesEnum.String, string.Empty, false));
+            AppendUserArgument(args, CS_ARG_DESC_USER);
+            AppendForceArgument(args, CS_ARG_DESC_FORCE);
+            return args.ToArray();
+        }
+
+        #endregion
+    }
+}

+ 1 - 1
Console/Properties/launchSettings.json

@@ -2,7 +2,7 @@
   "profiles": {
     "Console": {
       "commandName": "Project",
-      "commandLineArgs": "document_add -arts:doc1_file1.bin;doc1_file2.bin -force -user:test -dbg -dump"
+      "commandLineArgs": "document_clear -name:doc1_file1 -force -user:test -dbg -dump"
     }
   }
 }