瀏覽代碼

Add document, model ready, tested

Dalibor Votruba 4 年之前
父節點
當前提交
5a602f024f
共有 80 個文件被更改,包括 1650 次插入183 次删除
  1. 二進制
      @Documentation/api.coverage.odf
  2. 1 1
      AppServer/Business/Configuration/Configuration.cs
  3. 3 1
      AppServer/Business/ErrorCodes.txt
  4. 22 0
      AppServer/Business/Exceptions/CannotCastArgumentException.cs
  5. 19 0
      AppServer/Business/Exceptions/NoDataException.cs
  6. 41 5
      AppServer/Business/Mapper/MapperLong.cs
  7. 13 0
      AppServer/Business/Services/Base/UserContextService.cs
  8. 7 4
      AppServer/Business/Services/ConfigurationService.cs
  9. 30 0
      AppServer/Business/Services/DocumentService.cs
  10. 41 1
      AppServer/Business/Services/WorkspaceService.cs
  11. 25 3
      AppServer/Connector.Console/Connection.cs
  12. 121 55
      AppServer/Connector/AbstractConnection.cs
  13. 1 1
      AppServer/Data/Entity/Artifact.cs
  14. 2 0
      AppServer/Data/Entity/BOContext.cs
  15. 33 0
      AppServer/Data/Entity/BOContext.decl.cs
  16. 1 1
      AppServer/Data/Entity/BillingInfo.cs
  17. 1 1
      AppServer/Data/Entity/BillingPlan.cs
  18. 1 1
      AppServer/Data/Entity/Invoice.cs
  19. 1 1
      AppServer/Data/Entity/InvoiceIssuer.cs
  20. 1 1
      AppServer/Data/Entity/InvoiceItem.cs
  21. 1 1
      AppServer/Data/Entity/Metadocument.cs
  22. 1 1
      AppServer/Data/Entity/MimeType.cs
  23. 1 1
      AppServer/Data/Entity/Registration.cs
  24. 1 1
      AppServer/Data/Entity/Statistic.cs
  25. 1 1
      AppServer/Data/Entity/StatusHistory.cs
  26. 1 1
      AppServer/Data/Entity/Structure.cs
  27. 2 0
      AppServer/Data/Entity/Structure.decl.cs
  28. 1 1
      AppServer/Data/Entity/Tag.cs
  29. 1 1
      AppServer/Data/Entity/User.cs
  30. 2 0
      AppServer/Data/Entity/User.decl.cs
  31. 1 1
      AppServer/Data/Entity/Workspace.cs
  32. 1 1
      AppServer/Data/Entity/WorkspaceBilling.cs
  33. 5 0
      AppServer/Data/Repository/MetadocumentRepo.cs
  34. 40 0
      AppServer/Data/Repository/WorkspaceBillingRepo.cs
  35. 7 0
      AppServer/Data/Repository/WorkspaceRepo.cs
  36. 1 1
      AppServer/Data/readme.txt
  37. 11 11
      AppServer/Metadata/Dto/ArtifactDto.cs
  38. 2 4
      AppServer/Metadata/Dto/AssemblyInfoDto.cs
  39. 1 1
      AppServer/Metadata/Dto/AuthDto.cs
  40. 1 1
      AppServer/Metadata/Dto/BillingPlanDto.cs
  41. 17 5
      AppServer/Metadata/Dto/BoAuditDto.cs
  42. 1 2
      AppServer/Metadata/Dto/BoDto.cs
  43. 18 12
      AppServer/Metadata/Dto/DocumentDto.cs
  44. 1 1
      AppServer/Metadata/Dto/DocumentStatusHistoryDto.cs
  45. 11 0
      AppServer/Metadata/Dto/Dto.cs
  46. 1 1
      AppServer/Metadata/Dto/MimeTypeDto.cs
  47. 2 4
      AppServer/Metadata/Dto/PingDto.cs
  48. 1 2
      AppServer/Metadata/Dto/StatisticsDto.cs
  49. 52 0
      AppServer/Metadata/Dto/StructureDto.cs
  50. 1 1
      AppServer/Metadata/Dto/UserDto.cs
  51. 7 3
      AppServer/Metadata/Dto/WorkspaceDto.cs
  52. 27 0
      AppServer/Metadata/Extensions/ResultValueExt.cs
  53. 25 2
      AppServer/Web/Services/ConsoleController.cs
  54. 4 1
      AppServer/Web/Startup.cs
  55. 1 1
      AppServer/Web/appsettings.Development.json
  56. 19 0
      Common/qdr.fnd.core.data/Extensions/QueryableExt.cs
  57. 13 0
      Common/qdr.fnd.core.data/Mapper/Mapper.cs
  58. 8 4
      Common/qdr.fnd.core.data/Repository/EntityRepository.cs
  59. 593 0
      Common/qdr.fnd.core/IO/MimeTypes/MimeTypeUtil.cs
  60. 26 5
      Common/qdr.fnd.core/Object/Extensions/ObjectExt.cs
  61. 20 0
      Common/qdr.fnd.core/Reflection/Extensions/TypeExt.cs
  62. 1 1
      Console/Commands/Base/AbstractListScopedCommand.cs
  63. 2 3
      Console/Commands/Base/AbstractPostCommand.cs
  64. 71 0
      Console/Commands/Base/AbstractWorkspaceCommand.cs
  65. 58 7
      Console/Commands/Base/BaseCommand.cs
  66. 11 0
      Console/Commands/Base/SinglePaging.cs
  67. 2 2
      Console/Commands/Catalogues/MimeTypesGetCommand.cs
  68. 118 13
      Console/Commands/Document/DocumentAddCommand.cs
  69. 4 5
      Console/Commands/Document/DocumentsGetCommand.cs
  70. 41 0
      Console/Commands/Workspace/WorkspaceGetCommand.cs
  71. 1 2
      Console/Commands/Workspace/WorkspacesGetCommand.cs
  72. 1 0
      Console/Console.csproj
  73. 8 0
      Console/Constants.cs
  74. 19 0
      Console/Options/Configuration.cs
  75. 8 0
      Console/Program.cs
  76. 1 1
      Console/Properties/launchSettings.json
  77. 二進制
      Console/Samples/doc1_file1.bin
  78. 二進制
      Console/Samples/doc1_file2.bin
  79. 3 0
      Console/Samples/upload_document_2_art.cmd
  80. 7 1
      Console/console.json

二進制
@Documentation/api.coverage.odf


+ 1 - 1
AppServer/Business/Configuration/Configuration.cs

@@ -39,7 +39,7 @@ namespace BO.AppServer.Business.Configuration
 
         public class CfgBusiness
         {
-            public string DefaultBillingPlan { get; set; }
+            public string DefaultBillingPlanCode { get; set; }
         }
 
         public class CfgSystemStorage

+ 3 - 1
AppServer/Business/ErrorCodes.txt

@@ -4,7 +4,9 @@
 Business (1000 - 2000)
 1000	Entity '{0}' named '{1}' already exists in database.
 1001	Entity '{0}' named '{1}' not exists in database.
-1010	Entity '{0}' filed '{1}' maximal length overflow (max. {2})
+1010	Entity '{0}' filed '{1}' maximal length overflow (max. {2}).
+1011	Cannot cast to type '{2}', invalid argument '{0}' value ({1}).
+1099	No data returns but not expected.
 
 Business.UserService (1100 - 1199)
 1100	Cannot create user '{0}', user name or email already exists.

+ 22 - 0
AppServer/Business/Exceptions/CannotCastArgumentException.cs

@@ -0,0 +1,22 @@
+using System;
+using Quadarax.Foundation.Core.Exceptions;
+
+namespace BO.AppServer.Business.Exceptions
+{
+    // Invalid argument '{0}' value ({1}), cannot cast to type '{2}'.
+    public class CannotCastArgumentException : CodeException
+    {
+        private const string CS_MESSAGE = "Cannot cast to type '{2}', invalid argument '{0}' value ({1}).";
+        private const int CN_CODE = 1011;
+
+
+        public CannotCastArgumentException(string argumentName, string argumentValue,Type expectedType, Exception innerException) : base(CN_CODE, string.Format(CS_MESSAGE,argumentName, argumentValue, expectedType.Name), innerException)
+        {
+        }
+
+        public CannotCastArgumentException(string argumentName, string argumentValue,Type expectedType) : base(CN_CODE, string.Format(CS_MESSAGE,argumentName, argumentValue, expectedType.Name))
+        {
+        }
+
+    }
+}

+ 19 - 0
AppServer/Business/Exceptions/NoDataException.cs

@@ -0,0 +1,19 @@
+using System;
+using Quadarax.Foundation.Core.Exceptions;
+
+namespace BO.AppServer.Business.Exceptions
+{
+    public class NoDataException : CodeException
+    {
+        private const string CS_MESSAGE = "No data returns but not expected.";
+        private const int CN_CODE = 1099;
+
+        public NoDataException(Exception innerException) : base(CN_CODE, CS_MESSAGE)
+        {
+        }
+
+        public NoDataException() : base(CN_CODE, CS_MESSAGE)
+        {
+        }
+    }
+}

+ 41 - 5
AppServer/Business/Mapper/MapperLong.cs

@@ -1,6 +1,10 @@
-using BO.AppServer.Data.Entity;
+using System;
+using System.Linq;
+using BO.AppServer.Data.Entity;
 using BO.AppServer.Metadata.Dto;
+using BO.AppServer.Metadata.Enums;
 using Quadarax.Foundation.Core.Data.Interface.Entity;
+using Quadarax.Foundation.Core.Mapper;
 
 namespace BO.AppServer.Business.Mapper
 {
@@ -25,9 +29,21 @@ namespace BO.AppServer.Business.Mapper
 
             DefineBiDirection<Statistic,StatisticsDto>();
 
-            DefineBiDirection<Workspace, WorkspaceRDto>();
-            DefineBiDirection<Workspace, WorkspaceCDto>();
-            DefineBiDirection<Workspace, WorkspaceUDto>();
+            Define<Workspace, WorkspaceRDto>(builder =>
+                {
+                    builder.Custom(dest => dest.Structures, src => src.Structures.MapList<Structure, StructureRDto>());
+                    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);
+                });
+
+            Define<WorkspaceCDto, Workspace>();
+            Define<WorkspaceUDto, Workspace>();
 
 
             DefineBiDirection<Metadocument, DocumentRDto>();
@@ -35,7 +51,24 @@ namespace BO.AppServer.Business.Mapper
             
             DefineBiDirection<Artifact, ArtifactRDto>();
             DefineBiDirection<Artifact, ArtifactCDto>();
-            
+
+            Define<Structure, StructureRDto>(builder =>
+            {
+                builder.Custom(dest => dest.DocumentAllCount, src => src.Metadocuments.Count);
+                builder.Custom(dest => dest.DocumentPendingCount, src => src.Metadocuments.Count(x => x.Status == (int)DocumentStatusEnum.Pending));
+                builder.Custom(dest => dest.DocumentWorkingCount, src => src.Metadocuments.Count(x => x.Status == (int)DocumentStatusEnum.Processing));
+                builder.Custom(dest => dest.DocumentDoneCount, src => src.Metadocuments.Count(x => x.Status == (int)DocumentStatusEnum.DoneOk));
+                builder.Custom(dest => dest.DocumentFailCount, src => src.Metadocuments.Count(x => x.Status == (int)DocumentStatusEnum.DoneFail));
+                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
+                });
+
+            });
+
         }
         public void DefineBiDirection<TDao, TDto>() 
             where TDao : class, IDao<long>, new()
@@ -43,5 +76,8 @@ namespace BO.AppServer.Business.Mapper
         {
             DefineBiDirection<TDao, TDto, long>();
         }
+
+
+
     }
 }

+ 13 - 0
AppServer/Business/Services/Base/UserContextService.cs

@@ -1,10 +1,12 @@
 using System;
 using System.Security.Principal;
+using BO.AppServer.Business.Exceptions;
 using BO.AppServer.Data.Entity;
 using BO.AppServer.Metadata.Enums;
 using Microsoft.AspNetCore.Identity;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
+using NLog.LayoutRenderers;
 using Quadarax.Foundation.Core.Business;
 
 namespace BO.AppServer.Business.Services.Base
@@ -40,5 +42,16 @@ namespace BO.AppServer.Business.Services.Base
                 principal = new GenericPrincipal(new GenericIdentity(config.Value.System.AccountSystem.Name),new[] { RoleEnum.System.ToString() });
             return principal;
         }
+
+
+        protected long ArgumentAsLong(string argumentName, string value)
+        {
+            if (!long.TryParse(value, out var result))
+            {
+                throw new CannotCastArgumentException(argumentName, value, typeof(long));
+            }
+
+            return result;
+        }
     }
 }

+ 7 - 4
AppServer/Business/Services/ConfigurationService.cs

@@ -108,9 +108,9 @@ namespace BO.AppServer.Business.Services
 
                 var task2 = srvCatalogues.CreateMimeTypeAsync(new MimeTypeCDto()
                 {
-                    Mimetype1 = "text/plain",
-                    Description = "Plain text file",
-                    Extensions = "txt",
+                    Mimetype1 = "application/octet-stream",
+                    Description = "Binary files",
+                    Extensions = "bin",
                     IsEnabled = true
                 }, true);
 
@@ -130,7 +130,10 @@ namespace BO.AppServer.Business.Services
             return new ResultPlain();
         }
 
-
+        public void LogInfo(string message)
+        {
+            Log.LogInformation(message);
+        }
 
         protected async Task EnsureUser(UserService srvUsers, string userName, string userPassword, string userEmail, int prefferedLCID,
             RoleEnum[] roles)

+ 30 - 0
AppServer/Business/Services/DocumentService.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.IO.Abstractions;
 using System.Linq;
 using System.Security.Principal;
 using System.Threading.Tasks;
@@ -23,6 +24,8 @@ namespace BO.AppServer.Business.Services
     public class DocumentService : UserContextService
     {
         #region *** Private fields ***
+        private IFileSystem _fileSystem;
+
         private WorkspaceRepo _repoWorkspace;
         private MetadocumentRepo _repoDocument;
         private MimeTypeRepo _repoMType;
@@ -37,6 +40,8 @@ namespace BO.AppServer.Business.Services
             MetadocumentRepo repoDocument,
             MimeTypeRepo repoMType,
             ArtifactRepo repoArtifact,
+            StorageService srvStorage,
+            IFileSystem fileSystem,
             IOptions<Configuration.Configuration> config,
             UserRepo repoUser, 
             UserManager<IdentityUser> userManager, 
@@ -47,6 +52,9 @@ namespace BO.AppServer.Business.Services
             _repoDocument = repoDocument ?? throw new ArgumentNullException(nameof(repoDocument));
             _repoMType = repoMType ?? throw new ArgumentNullException(nameof(repoMType));
             _repoArtifact = repoArtifact ??  throw new ArgumentNullException(nameof(repoArtifact));
+            _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
+
+            _srvStorage = srvStorage ?? throw new ArgumentNullException(nameof(srvStorage));
         }
         #endregion
 
@@ -85,6 +93,17 @@ namespace BO.AppServer.Business.Services
             return new ResultsValueDto<DocumentRDto>(documents.MapList<Metadocument, DocumentRDto>());
         }
 
+
+        public async Task<ResultValueDto<DocumentRDto>> GetDocumentByNameAsync(long workspaceId,  string name)
+        {
+            CheckAndGetWorkspace(workspaceId);
+            IList<Metadocument> documents = new List<Metadocument>();
+            var document = _repoDocument.GetByName(workspaceId, name);
+            if (document == null)
+                throw new NoDataException();
+            return new ResultValueDto<DocumentRDto>(document.Map<Metadocument, DocumentRDto>());
+        }
+
         public async Task<ResultsValueDto<DocumentRDto>> GetAllDocumentsAsync(DocumentsScopeEnums scope, PagingDto paging)
         {
             IList<Metadocument> documents = new List<Metadocument>();
@@ -134,6 +153,7 @@ namespace BO.AppServer.Business.Services
             return new ResultValueDto<DocumentRDto>(newDocument.Map<Metadocument, DocumentRDto>());
         }
 
+
         public async Task<ResultValueDto<ArtifactRDto>> AppendArtifactAsync(long workspaceId, long documentId,
             ArtifactCDto artifact, ArtifactTypeEnum type, Stream content)
         {
@@ -182,6 +202,7 @@ namespace BO.AppServer.Business.Services
 
         }
 
+
         public async Task<Tuple<Stream, string>> GetArtifactAsync(long workspaceId, long documentId, long artifactId)
         {
             var artifact = CheckAndGetArtifact(workspaceId, documentId, artifactId);
@@ -190,6 +211,15 @@ namespace BO.AppServer.Business.Services
             return new Tuple<Stream, string>(result.Item1, artifact.MimeType.Mimetype1);
 
         }
+
+        public ArtifactCDto CreateArtifactDto(string fileName, string contentType)
+        {
+            var result = new ArtifactCDto();
+            result.Extension = _fileSystem.Path.GetExtension(fileName);
+            result.MimeType = contentType;
+            result.Name = _fileSystem.Path.GetFileNameWithoutExtension(fileName);
+            return result;
+        }
         #endregion
 
         #region *** Private Operations ***

+ 41 - 1
AppServer/Business/Services/WorkspaceService.cs

@@ -8,6 +8,7 @@ using BO.AppServer.Business.Exceptions;
 using BO.AppServer.Business.Mapper;
 using BO.AppServer.Business.Services.Base;
 using BO.AppServer.Data.Entity;
+using BO.AppServer.Data.Repository;
 using BO.AppServer.Metadata.Dto;
 using BO.AppServer.Metadata.Enums;
 using Microsoft.AspNetCore.Identity;
@@ -25,11 +26,13 @@ namespace BO.AppServer.Business.Services
         #region *** Private fields ***
         private readonly WorkspaceRepo _repoWorkspace;
         private readonly BillingPlanRepo _repoBillingPlan;
+        private readonly WorkspaceBillingRepo _repoWorkspaceBilling;
         #endregion
 
         #region *** Constructor ***
         public WorkspaceService(WorkspaceRepo repoWorkspace, 
             BillingPlanRepo repoBillingPlan,
+            WorkspaceBillingRepo repoWorkspaceBilling,
             IOptions<Configuration.Configuration> config,
             UserRepo repoUser, 
             UserManager<IdentityUser> userManager, 
@@ -38,7 +41,7 @@ namespace BO.AppServer.Business.Services
         {
             _repoWorkspace = repoWorkspace ?? throw new ArgumentNullException(nameof(repoWorkspace));
             _repoBillingPlan = repoBillingPlan ?? throw new ArgumentNullException(nameof(repoBillingPlan));
-
+            _repoWorkspaceBilling = repoWorkspaceBilling ??  throw new ArgumentNullException(nameof(repoWorkspaceBilling));
         }
         #endregion
 
@@ -124,6 +127,43 @@ namespace BO.AppServer.Business.Services
             return workspace.Id;
         }
 
+        public async Task<ResultValueDto<WorkspaceRDto>> GetWorkspace(IdentificationTypeEnum identificationType, string identificationValue)
+        {
+            var workspace = identificationType == IdentificationTypeEnum.Id ? 
+                _repoWorkspace.Get(ArgumentAsLong(nameof(identificationValue), identificationValue)) 
+                : _repoWorkspace.GetByName(identificationValue);
+
+            
+            if (workspace == null)
+                throw new NoDataException();
+
+            var currentBillingPlan = GetCurrentWorkspaceBillingOrCreate(workspace.Id);
+
+            // gather rest of data
+            var result = new ResultValueDto<WorkspaceRDto>(workspace.Map<Workspace, WorkspaceRDto>());
+            result.Value.CreditAmount = currentBillingPlan.ValueNumber;
+            result.Value.BillingPlanCode = currentBillingPlan.BillingPlan.Code;
+            return result;
+        }
+
+        #endregion
+
+
+        #region *** Private Operations ***
+
+        private WorkspaceBilling GetCurrentWorkspaceBillingOrCreate(long workspaceId)
+        {
+            var currentBillingPlan = _repoWorkspaceBilling.GetByCurrentForWorkspace(workspaceId);
+            if (currentBillingPlan == null)
+            {
+                currentBillingPlan = _repoWorkspaceBilling.CreateForWorkspace(workspaceId,
+                    _repoBillingPlan.GetByCode(_configuration.Business.DefaultBillingPlanCode));
+                _repoWorkspaceBilling.Commit();
+                Log.LogWarning($"Default billing plan was not set for workspace [{workspaceId}]. Set default billing plan '{currentBillingPlan.BillingPlan.Code}'.");
+            }
+
+            return currentBillingPlan;
+        }
         #endregion
     }
 }

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

@@ -4,6 +4,7 @@ using System.IO;
 using System.Threading.Tasks;
 using BO.AppServer.Metadata.Dto;
 using BO.AppServer.Metadata.Enums;
+using BO.AppServer.Metadata.Extensions;
 using Quadarax.Foundation.Core.Data.Interface;
 using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;
 using Quadarax.Foundation.Core.Logging;
@@ -136,6 +137,17 @@ namespace BO.Connector.Console
             var result = await CallRequestAsync<ResultsValueDto<DocumentRDto>>($"{Ticket}/workspace/{workspaceApiKey}/documents/{scope}", RestCallTypeEnum.Get,null, header);
             return result.Values;
         }
+
+        public async Task<DocumentRDto> GetWorkspaceDocumentByNameAsync(string workspaceApiKey,string apiKeyPassword,string documentName, string userNameContext)
+        {
+            CheckIsOpen();
+            var header = GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword));
+            header.Add("documentName", documentName);
+            var result = await CallRequestAsync<ResultValueDto<DocumentRDto>>($"{Ticket}/workspace/{workspaceApiKey}/document", RestCallTypeEnum.Get,null, header);
+            // can return null value
+            return result.HasNoDataException() ? null : result.Value;
+        }
+
         public async Task<DocumentRDto> GetWorkspaceDocumentAsync(string workspaceApiKey,string apiKeyPassword,long documentId, string userNameContext)
         {
             CheckIsOpen();
@@ -144,6 +156,7 @@ namespace BO.Connector.Console
             return result.Value;
         }
 
+
         public async Task<Stream> GetWorkspaceDocumentContentAsync(string workspaceApiKey,string apiKeyPassword,long documentId, long artifactId, string userNameContext)
         {
             CheckIsOpen();
@@ -160,14 +173,14 @@ namespace BO.Connector.Console
             return result.Value;
         }
 
-        public async Task<ArtifactRDto> AppendWorkspaceDocumentArtifactAsync(string workspaceApiKey,string apiKeyPassword,long documentId, ArtifactCDto data,Stream contentStream, string userNameContext)
+        public async Task<ArtifactRDto> AppendWorkspaceDocumentArtifactAsync(string workspaceApiKey,string apiKeyPassword,long documentId, string fileName,string mimeType, Stream contentStream, string userNameContext)
         {
             CheckIsOpen();
             var header = GetUserContextHeaderAttributes(userNameContext, GetApiPasswordHeaderAttributes(apiKeyPassword));
-            var result = await CallPostStreamMultipartAsync<ResultValueDto<ArtifactRDto>>($"{Ticket}/workspace/{workspaceApiKey}/document/{documentId}/artifact/input", data, contentStream, header);
+            var result = await CallPostStreamMultipartAsync<ResultValueDto<ArtifactRDto>>($"{Ticket}/workspace/{workspaceApiKey}/document/{documentId}/artifact/input", fileName, contentStream,mimeType, header);
             return result.Value;
         }
-
+         
         #endregion
 
 
@@ -179,6 +192,14 @@ namespace BO.Connector.Console
             return result.Value;
         }
 
+        public async Task<WorkspaceRDto> GetWorkspaceAsync(IdentificationTypeEnum identificationType, string identificationValue)
+        {
+            CheckIsOpen();
+            var result = await CallRequestAsync<ResultValueDto<WorkspaceRDto>>($"{Ticket}/workspace/{identificationType}/{identificationValue}", RestCallTypeEnum.Get);
+            return result.Value;
+        }
+
+
         public async Task<IEnumerable<WorkspaceRDto>> GetWorkspacesAsync(WorkspaceScopeEnums scope,string userNameContext, PagingDto paging = null)
         {
             CheckIsOpen();
@@ -186,6 +207,7 @@ namespace BO.Connector.Console
             var result = await CallRequestAsync<ResultsValueDto<WorkspaceRDto>>($"{Ticket}/workspaces/{scope}", RestCallTypeEnum.Get,null,headers);
             return result.Values;
         }
+
         #endregion
 
     }

+ 121 - 55
AppServer/Connector/AbstractConnection.cs

@@ -2,8 +2,10 @@
 using System.Collections.Generic;
 using System.IO;
 using System.IO.Abstractions;
+using System.Linq;
 using System.Net.Http;
 using System.Net.Http.Headers;
+using System.Text;
 using System.Threading.Tasks;
 using BO.AppServer.Metadata.Dto;
 using Quadarax.Foundation.Core.Data.Interface;
@@ -135,12 +137,10 @@ namespace BO.Connector
             if (source == null)
                 source = new Dictionary<string, string>();
 
-            var result = new Dictionary<string, string>
-            {
-                { "page", paging.Page.ToString() },
-                { "pageSize", paging.PageSize.ToString() }
-            };
-            return result;
+
+            source.Add(new KeyValuePair<string, string>("page", paging.Page.ToString()));
+            source.Add(new KeyValuePair<string, string>("pageSize", paging.PageSize.ToString()));
+            return source;
         }
 
         protected IDictionary<string, string> GetApiPasswordHeaderAttributes(string apiKeyPassword, IDictionary<string,string> source = null)
@@ -148,11 +148,8 @@ namespace BO.Connector
             if (source == null)
                 source = new Dictionary<string, string>();
 
-            var result = new Dictionary<string, string>
-            {
-                { "apiKeyPassword", apiKeyPassword },
-            };
-            return result;
+            source.Add(new KeyValuePair<string, string>("apiKeyPassword", apiKeyPassword));
+            return source;
         }
 
         protected IDictionary<string, string> GetUserContextHeaderAttributes(string userNameContext, IDictionary<string,string> source = null)
@@ -160,11 +157,8 @@ namespace BO.Connector
             if (source == null)
                 source = new Dictionary<string, string>();
 
-            var result = new Dictionary<string, string>
-            {
-                { "context", userNameContext },
-            };
-            return result;
+            source.Add(new KeyValuePair<string, string>("context", userNameContext));
+            return source;
         }
 
 
@@ -187,50 +181,67 @@ namespace BO.Connector
         }
 
         protected async Task<TResult> CallPostStreamMultipartAsync<TResult>(string relativeUrlApiCall,
-            IDto data,Stream stream, IDictionary<string, string> headerAttributes = null, bool silent = false)
+            string fileName,Stream stream,string streamMimeType, IDictionary<string, string> headerAttributes = null, bool silent = false)
             where TResult : ResultDto
         {
             Log(LogSeverityEnum.Trace, $"Calling REST API POST-MULTI-STREAM {_uriApiBase.AbsoluteUri + relativeUrlApiCall}");
 
-            var rqData = new StringContent(_binder.SaveToString(data));
-            var rqStream = new StreamContent(stream);
-            var req = new MultipartContent();
-            req.Add(rqData);
-            req.Add(rqStream);
-            if (headerAttributes!=null)
-                foreach (var key in headerAttributes.Keys)
-                    req.Headers.Add(key,headerAttributes[key]);
-            req.Headers.ContentType = new MediaTypeHeaderValue("application/json");
-            var start = DateTime.Now;
-            var resp = await _client.PostAsync(relativeUrlApiCall, req);
-         
-         
-            Log(LogSeverityEnum.Trace, $"REST API POST-MULTI-STREAM {_uriApiBase.AbsoluteUri + relativeUrlApiCall} done @ {(DateTime.Now - start).ToReadableString()}");
-            if (!resp.IsSuccessStatusCode)
-            {
-                var message = $"Calling REST API POST-MULTI-STREAM {_uriApiBase.AbsoluteUri + relativeUrlApiCall} ends with result code '{resp.StatusCode}'";
-                Log(LogSeverityEnum.Warn, message);
-                throw new Exception(message);
-            }
 
-            var content = await resp.Content.ReadAsStringAsync();
-            if (_isDumpContentEnabled)
-                Log(LogSeverityEnum.Trace, content);
+            var boundary = Guid.NewGuid().ToString();
+            var mpc = new MultipartFormDataContent(boundary);
+            mpc.Headers.Remove("Content-Type");
+            mpc.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary);
 
-            var result = (TResult)_binder.LoadFromString(content, typeof(TResult));
-            if (result.IsSuccess) return result;
-            if (silent) return result;
 
-            // handle errors
-            var excps = new List<Exception>();
-            foreach (var err in result.Errors)
+            var stc = new StreamContent(stream);
+            stc.Headers.ContentType = new MediaTypeHeaderValue(streamMimeType);
+            mpc.Add(stc, "content", fileName);
+
+
+            using (var rq = new HttpRequestMessage())
             {
-                var exc = new Exception($"[{err.Code}] {err.Message}");
-                exc.Data.Add("Code", err.Code);
-                excps.Add(exc);
-            }
+                rq.Content = mpc;
+                rq.Method = new System.Net.Http.HttpMethod("POST");
+                rq.RequestUri = new Uri(_uriApiBase.AbsoluteUri + relativeUrlApiCall);
+                rq.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/octet-stream"));
+                if (headerAttributes != null)
+                    foreach (var key in headerAttributes.Keys)
+                        rq.Headers.Add(key, headerAttributes[key]);
+
+                var start = DateTime.Now;
+                var resp = await _client.SendAsync(rq);
+                //var resp = await cli.PostAsync(relativeUrlApiCall, rqdMulti);
+
+
+                Log(LogSeverityEnum.Trace,
+                    $"REST API POST-MULTI-STREAM {_uriApiBase.AbsoluteUri + relativeUrlApiCall} done @ {(DateTime.Now - start).ToReadableString()}");
+                if (!resp.IsSuccessStatusCode)
+                {
+                    var message =
+                        $"Calling REST API POST-MULTI-STREAM {_uriApiBase.AbsoluteUri + relativeUrlApiCall} ends with result code '{resp.StatusCode}'";
+                    Log(LogSeverityEnum.Warn, message);
+                    throw new Exception(message);
+                }
 
-            throw new AggregateException(excps);
+                var content = await resp.Content.ReadAsStringAsync();
+                if (_isDumpContentEnabled)
+                    Log(LogSeverityEnum.Trace, content);
+
+                var result = (TResult)_binder.LoadFromString(content, typeof(TResult));
+                if (result.IsSuccess) return result;
+                if (silent) return result;
+
+                // handle errors
+                var excps = new List<Exception>();
+                foreach (var err in result.Errors)
+                {
+                    var exc = new Exception($"[{err.Code}] {err.Message}");
+                    exc.Data.Add("Code", err.Code);
+                    excps.Add(exc);
+                }
+
+                throw new AggregateException(excps);
+            }
         }
 
 
@@ -245,6 +256,8 @@ namespace BO.Connector
             var start = DateTime.Now;
             HttpResponseMessage resp = null;
 
+            SetDefaultClientHeaders(_client);
+            HttpContentHeaders reqHeaders = null;
             switch (method)
             {
                 case RestCallTypeEnum.Get:
@@ -260,17 +273,31 @@ namespace BO.Connector
                     break;
                 case RestCallTypeEnum.Post:
                     var req = new StringContent(_binder.SaveToString(data));
+                    reqHeaders = req.Headers;
                     if (headerAttributes!=null)
                         foreach (var key in headerAttributes.Keys)
-                            req.Headers.Add(key,headerAttributes[key]);
+                        {
+                            //if (req.Headers.Contains(key))
+                                req.Headers.Remove(key);
+
+                            req.Headers.Add(key, headerAttributes[key]);
+                        }
+
                     req.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                     resp = await _client.PostAsync(relativeUrlApiCall, req);
                     break;
                 case RestCallTypeEnum.Put:
                     req = new StringContent(_binder.SaveToString(data));
+                    reqHeaders = req.Headers;
                     if (headerAttributes!=null)
                         foreach (var key in headerAttributes.Keys)
-                            req.Headers.Add(key,headerAttributes[key]);
+                        {
+
+                         //   if (req.Headers.Contains(key))
+                                req.Headers.Remove(key);
+
+                            req.Headers.Add(key, headerAttributes[key]);
+                        }
                     req.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                     resp = await _client.PutAsync(relativeUrlApiCall, req);
                     break;
@@ -299,10 +326,15 @@ namespace BO.Connector
 
             var content = await resp.Content.ReadAsStringAsync();
             if (_isDumpContentEnabled)
+            {
+                Log(LogSeverityEnum.Trace, DumpHttpClient(method,relativeUrlApiCall, _client, reqHeaders));
                 Log(LogSeverityEnum.Trace, content);
+            }
 
             var result = (TResult)_binder.LoadFromString(content, typeof(TResult));
             if (result.IsSuccess) return result;
+            // No data exception pass result
+            if (!result.IsSuccess && result.Errors.Any(x => x.Code == 1099)) return result;
             if (silent) return result;
 
             // handle errors
@@ -316,6 +348,8 @@ namespace BO.Connector
 
             throw new AggregateException(excps);
         }
+
+
         protected virtual void OnOpening()
         {
         }
@@ -325,13 +359,45 @@ namespace BO.Connector
             var client = new HttpClient();
             client.BaseAddress = new Uri(_uriApiBase, ApiName);
             client.Timeout = _timeout;
-            client.DefaultRequestHeaders.Accept.Clear();
+            SetDefaultClientHeaders(client);
+            return client;
+        }
+
+        private void SetDefaultClientHeaders(HttpClient client)
+        {
+            client.DefaultRequestHeaders.Clear();
             client.DefaultRequestHeaders.Add("User-Agent", "BO.Connector." + ApiName);
             // Add an Accept header for JSON format.
             client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
-            return client;
+            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("multipart/mixed"));
+            //client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
         }
 
+        private string DumpHttpClient(RestCallTypeEnum method, string relativeUrl, HttpClient client, HttpContentHeaders headers)
+        {
+            var sb = new StringBuilder();
+            sb.Append($"RQ {method.ToString().ToUpper()} to url '{_client.BaseAddress + relativeUrl}':").AppendLine();
+            sb.Append($"rq version: {_client.DefaultRequestVersion}").AppendLine();
+            sb.Append($"rq timeout: {_client.Timeout.ToReadableString()}").AppendLine();
+            sb.Append("Headers:").AppendLine();
+            foreach (var header in _client.DefaultRequestHeaders)
+                sb.Append(header.Key).Append(" : ").Append(string.Join(",", header.Value)).AppendLine();
+            sb.Append(DumpHttpHeaders(headers));
+            return sb.ToString();
+        }
+
+        private string DumpHttpHeaders(HttpContentHeaders headers)
+        {
+            var sb = new StringBuilder();
+            sb.Append("Headers:").AppendLine();
+            if (headers != null)
+            {
+                foreach (var header in headers)
+                    sb.Append(header.Key).Append(" : ").Append(string.Join(",", header.Value)).AppendLine();
+            }
+
+            return sb.ToString();
+        }
         #endregion
 
     }

+ 1 - 1
AppServer/Data/Entity/Artifact.cs

@@ -7,7 +7,7 @@ namespace BO.AppServer.Data.Entity
 {
     public partial class Artifact
     {
-        // public long Id { get; set; }
+        // // public long Id { get; set; }
         public long MetadocumentId { get; set; }
         public string Name { get; set; }
         public string Extension { get; set; }

+ 2 - 0
AppServer/Data/Entity/BOContext.cs

@@ -181,6 +181,7 @@ namespace BO.AppServer.Data.Entity
                     .HasForeignKey(d => d.ModifiedByUserId)
                     .HasConstraintName("REL_BillingInfo_User_Modified");
 
+
                 //entity.HasOne(d => d.Workspace)
                 //    .WithMany(p => p.BillingInfos)
                 //    .HasForeignKey(d => d.WorkspaceId)
@@ -1151,6 +1152,7 @@ namespace BO.AppServer.Data.Entity
                     .WithMany(p => p.WorkspaceModifiedByUsers)
                     .HasForeignKey(d => d.ModifiedByUserId)
                     .HasConstraintName("REL_Workspace_User_Modified");
+
             });
 
             modelBuilder.Entity<WorkspaceBilling>(entity =>

+ 33 - 0
AppServer/Data/Entity/BOContext.decl.cs

@@ -26,12 +26,45 @@ namespace BO.AppServer.Data.Entity
                         .WithMany(m => m.UserWorkspaces)
                         .HasForeignKey(am => am.WorkspaceId).HasConstraintName("REL_BillingInfo_Workspace");
 
+
                     //modelBuilder.Entity<BillingInfo>().Metadata.AddIgnored("Workspace");
                     modelBuilder.Entity<Workspace>().HasOne(d => d.BillingInfo)
                     .WithOne(p => p.Workspace)
                     .HasForeignKey<BillingInfo>(a => a.WorkspaceId)
                     .OnDelete(DeleteBehavior.NoAction)
                     .HasConstraintName("REL_Workspace_BillingInfo");
+                    modelBuilder.Entity<Workspace>().HasMany(w=>w.Structures)
+                        .WithOne(s => s.Workspace)
+                        .HasForeignKey(s => s.WorkspaceId)
+                        .HasConstraintName("REL_Structure_Workspace");
+                    modelBuilder.Entity<Workspace>().Navigation(w => w.Structures).AutoInclude();
+                    modelBuilder.Entity<Workspace>().Navigation(w => w.CreatedByUser).AutoInclude();
+                    modelBuilder.Entity<Workspace>().Navigation(w => w.ModifiedByUser).AutoInclude();
+
+
+                    modelBuilder.Entity<Structure>().Navigation(w => w.Metadocuments).AutoInclude();
+                    modelBuilder.Entity<Structure>().HasOne(d => d.CreatedByUser)
+                        .WithMany(p => p.StructureCreatedByUsers)
+                        .HasForeignKey(d => d.CreatedByUserId)
+                        .OnDelete(DeleteBehavior.ClientSetNull)
+                        .HasConstraintName("REL_Structure_User_Created");
+                    modelBuilder.Entity<Structure>().HasOne(d => d.ModifiedByUser)
+                        .WithMany(p => p.StructureModifiedByUsers)
+                        .HasForeignKey(d => d.ModifiedByUserId)
+                        .HasConstraintName("REL_Structure_User_Modified");
+
+                    modelBuilder.Entity<Metadocument>().Navigation(w => w.CreatedByUser).AutoInclude();
+                    modelBuilder.Entity<Metadocument>().Navigation(w => w.ModifiedByUser).AutoInclude();
+
+
+                    modelBuilder.Entity<Artifact>().Navigation(w => w.CreatedByUser).AutoInclude();
+
+
+                    modelBuilder.Entity<BillingPlan>().Navigation(w => w.CreatedByUser).AutoInclude();
+                    modelBuilder.Entity<BillingPlan>().Navigation(w => w.ModifiedByUser).AutoInclude();
+
+                    modelBuilder.Entity<MimeType>().Navigation(w => w.CreatedByUser).AutoInclude();
+                    modelBuilder.Entity<MimeType>().Navigation(w => w.ModifiedByUser).AutoInclude();
                 }
             );
         }

+ 1 - 1
AppServer/Data/Entity/BillingInfo.cs

@@ -12,7 +12,7 @@ namespace BO.AppServer.Data.Entity
             Workspaces = new HashSet<Workspace>();
         }
 
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public long WorkspaceId { get; set; }
         public string SubjectName { get; set; }
         public string Address1 { get; set; }

+ 1 - 1
AppServer/Data/Entity/BillingPlan.cs

@@ -13,7 +13,7 @@ namespace BO.AppServer.Data.Entity
             WorkspaceBillings = new HashSet<WorkspaceBilling>();
         }
 
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public string Name { get; set; }
         public string Code { get; set; }
         public string Description { get; set; }

+ 1 - 1
AppServer/Data/Entity/Invoice.cs

@@ -12,7 +12,7 @@ namespace BO.AppServer.Data.Entity
             InvoiceItems = new HashSet<InvoiceItem>();
         }
 
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public long WorkspaceId { get; set; }
         public long InvoiceIssuerId { get; set; }
         public string Code { get; set; }

+ 1 - 1
AppServer/Data/Entity/InvoiceIssuer.cs

@@ -12,7 +12,7 @@ namespace BO.AppServer.Data.Entity
             Invoices = new HashSet<Invoice>();
         }
 
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public string SubjectName { get; set; }
         public string Address1 { get; set; }
         public string Address2 { get; set; }

+ 1 - 1
AppServer/Data/Entity/InvoiceItem.cs

@@ -7,7 +7,7 @@ namespace BO.AppServer.Data.Entity
 {
     public partial class InvoiceItem
     {
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public long InvoiceId { get; set; }
         public long BillingPlanId { get; set; }
         public string BillingPlanCode { get; set; }

+ 1 - 1
AppServer/Data/Entity/Metadocument.cs

@@ -13,7 +13,7 @@ namespace BO.AppServer.Data.Entity
             StatusHistories = new HashSet<StatusHistory>();
         }
 
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public long StructureId { get; set; }
         public string Name { get; set; }
         public int Status { get; set; }

+ 1 - 1
AppServer/Data/Entity/MimeType.cs

@@ -12,7 +12,7 @@ namespace BO.AppServer.Data.Entity
             Artifacts = new HashSet<Artifact>();
         }
 
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public string Mimetype1 { get; set; }
         public string Extensions { get; set; }
         public string Description { get; set; }

+ 1 - 1
AppServer/Data/Entity/Registration.cs

@@ -7,7 +7,7 @@ namespace BO.AppServer.Data.Entity
 {
     public partial class Registration
     {
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public string Psinstance { get; set; }
         public string LocationIp { get; set; }
         public DateTime? LastActivity { get; set; }

+ 1 - 1
AppServer/Data/Entity/Statistic.cs

@@ -7,7 +7,7 @@ namespace BO.AppServer.Data.Entity
 {
     public partial class Statistic
     {
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public int Year { get; set; }
         public int Month { get; set; }
         public int Day { get; set; }

+ 1 - 1
AppServer/Data/Entity/StatusHistory.cs

@@ -7,7 +7,7 @@ namespace BO.AppServer.Data.Entity
 {
     public partial class StatusHistory
     {
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public long MetadocumentId { get; set; }
         public int Status { get; set; }
         public string Message { get; set; }

+ 1 - 1
AppServer/Data/Entity/Structure.cs

@@ -13,7 +13,7 @@ namespace BO.AppServer.Data.Entity
             Metadocuments = new HashSet<Metadocument>();
         }
 
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public string Name { get; set; }
         public long WorkspaceId { get; set; }
         public long? ParentId { get; set; }

+ 2 - 0
AppServer/Data/Entity/Structure.decl.cs

@@ -5,5 +5,7 @@ namespace BO.AppServer.Data.Entity
 {
     public partial class Structure :  Entity<long>, IDao<long>
     {
+        public virtual User CreatedByUser { get; set; }
+        public virtual User ModifiedByUser { get; set; }
     }
 }

+ 1 - 1
AppServer/Data/Entity/Tag.cs

@@ -7,7 +7,7 @@ namespace BO.AppServer.Data.Entity
 {
     public partial class Tag
     {
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public long WorkspaceId { get; set; }
         public string Name { get; set; }
         public string Rgb { get; set; }

+ 1 - 1
AppServer/Data/Entity/User.cs

@@ -32,7 +32,7 @@ namespace BO.AppServer.Data.Entity
             WorkspaceModifiedByUsers = new HashSet<Workspace>();
         }
 
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public string Ifreference { get; set; }
         public string Name { get; set; }
         public DateTime? LastLogged { get; set; }

+ 2 - 0
AppServer/Data/Entity/User.decl.cs

@@ -7,5 +7,7 @@ namespace BO.AppServer.Data.Entity
     public partial class User :  Entity<long>, IDao<long>
     {
         public virtual ICollection<UserWorkspace> UserWorkspaces { get; set; }
+        public virtual ICollection<Structure> StructureCreatedByUsers { get; set; }
+        public virtual ICollection<Structure> StructureModifiedByUsers { get; set; }
     }
 }

+ 1 - 1
AppServer/Data/Entity/Workspace.cs

@@ -16,7 +16,7 @@ namespace BO.AppServer.Data.Entity
             WorkspaceBillings = new HashSet<WorkspaceBilling>();
         }
 
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public string Name { get; set; }
         public long? BillingInfoId { get; set; }
         public long BillingPlanId { get; set; }

+ 1 - 1
AppServer/Data/Entity/WorkspaceBilling.cs

@@ -7,7 +7,7 @@ namespace BO.AppServer.Data.Entity
 {
     public partial class WorkspaceBilling
     {
-        public long Id { get; set; }
+        // public long Id { get; set; }
         public long WorkspaceId { get; set; }
         public long BillingPlanId { get; set; }
         public DateTime Created { get; set; }

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

@@ -21,6 +21,11 @@ public class MetadocumentRepo : EntityRepository<long,  Metadocument>
                 return null;
             return document;
 
+        }
+        public Metadocument GetByName(long workspaceId, string name)
+        {
+            return Query(new Paging()).FirstOrDefault(x => x.Structure.WorkspaceId == workspaceId && x.Name .ToLower()== name.ToLower());
+
         }
 
         public Artifact GetArtifact(long metadocumentId, long artifactId)

+ 40 - 0
AppServer/Data/Repository/WorkspaceBillingRepo.cs

@@ -0,0 +1,40 @@
+using System;
+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;
+using Quadarax.Foundation.Core.Data.Interface.Domain;
+using Quadarax.Foundation.Core.Data.Repository;
+
+namespace BO.AppServer.Data.Repository
+{
+    public class WorkspaceBillingRepo : EntityRepository<long,  WorkspaceBilling>
+    {
+        public WorkspaceBillingRepo(IUnitOfWork<DataDomain> unitOfWork) : base(unitOfWork)
+        {
+            //DefaultIncludes.Add("Workspace");
+            DefaultIncludes.Add("BillingPlan");
+        }
+
+        public IQueryable<WorkspaceBilling> GetByWorkspace(long workspaceId, IPaging paging)
+        {
+            return Query(paging).Where(x => x.WorkspaceId == workspaceId);
+        }
+
+        public WorkspaceBilling GetByCurrentForWorkspace(long workspaceId)
+        {
+            return Query(new Paging()).Where(x => x.WorkspaceId == workspaceId).OrderByDescending(x=>x.Created).FirstOrDefault();
+        }
+
+        public WorkspaceBilling CreateForWorkspace(long workspaceId, BillingPlan bplan)
+        {
+            var newBp = New();
+            newBp.Created = DateTime.Now;
+            newBp.BillingPlan = bplan;
+            newBp.ValueNumber = bplan.PstinitialCredits;
+            newBp.WorkspaceId = workspaceId;
+            return newBp;
+        }
+    }
+}

+ 7 - 0
AppServer/Data/Repository/WorkspaceRepo.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 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.Extensions;
 using Quadarax.Foundation.Core.Data.Interface;
@@ -13,6 +14,7 @@ public class WorkspaceRepo : EntityRepository<long,  Workspace>
     {
         public WorkspaceRepo(IUnitOfWork<DataDomain> unitOfWork) : base(unitOfWork)
         {
+            DefaultIncludes.Add("Structures");
         }
 
         public Workspace CreateWorkspace(string name, BillingPlan billingPlan, string apiKeyPassword, bool isDefault, User owner, User creator)
@@ -57,6 +59,11 @@ public class WorkspaceRepo : EntityRepository<long,  Workspace>
             return Query(paging).Where(x => x.Structures.Any(struc => struc.Metadocuments.Count>0));
         }
 
+        public Workspace GetByName(string name)
+        {
+            return Query(new Paging()).FirstOrDefault(x => x.Name.ToLower() == name.ToLower());
+        }
+
         public IQueryable<Workspace> GetAllByOwner(User owner, IPaging paging)
         {
             var userWorkspaceSet = Context.GetDbSet<DbSet<UserWorkspace>>();

+ 1 - 1
AppServer/Data/readme.txt

@@ -7,7 +7,7 @@ To reverse update:
 - Run following command
 Scaffold-DbContext "Server=(local);Database=BO;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entity -Force
 - Go to file BOContext.cs and replace class declaration as : public partial class BOContext : DataDomain
-- replace in all entities // // public long Id { get; set; } -> // // // public long Id { get; set; }
+- replace in all entities // // // public long Id { get; set; } -> // // // // public long Id { get; set; }
 - replace in BOContext.cs occurency [ln cca:1069]:
 modelBuilder.Entity<UserWorkspace>(entity =>
             {

+ 11 - 11
AppServer/Metadata/Dto/ArtifactDto.cs

@@ -2,7 +2,7 @@
 
 namespace BO.AppServer.Metadata.Dto
 {
-    #region *** Artifact - Create ***
+    #region *** Artifact - Base ***
 
     public class ArtifactBaseDto : BoDto
     {
@@ -12,11 +12,11 @@ namespace BO.AppServer.Metadata.Dto
 
     #region *** Artifact - Create ***
 
-    public class ArtifactCDto : IDto, ICreateDto
+    public class ArtifactCDto : Dto, ICreateDto
     {
-        public string Name { get; }
-        public string Extension { get; }
-        public string MimeType { get; }
+        public string Name { get; set; }
+        public string Extension { get; set;}
+        public string MimeType { get; set;}
     }
 
     #endregion
@@ -33,12 +33,12 @@ namespace BO.AppServer.Metadata.Dto
 
     public class ArtifactRDto : ArtifactBaseDto, IReadDto
     {
-        public BoAuditDto Audit { get; }        
-        public string Name { get; }
-        public string Extension { get; }
-        public string MimeType { get; }
-        public string MimeTypeDescription { get; }
-        public long Length { get; }
+        public BoAuditDto Audit { get; set; }        
+        public string Name { get; set;}
+        public string Extension { get; set;}
+        public string MimeType { get; set;}
+        public string MimeTypeDescription { get; set;}
+        public long Length { get; set;}
     }
 
     #endregion

+ 2 - 4
AppServer/Metadata/Dto/AssemblyInfoDto.cs

@@ -1,8 +1,6 @@
-using Quadarax.Foundation.Core.Data.Interface.Entity;
-
-namespace BO.AppServer.Metadata.Dto
+namespace BO.AppServer.Metadata.Dto
 {
-    public class AssemblyInfoDto : IDto
+    public class AssemblyInfoDto : Dto
     {
         public string Name { get; set; }
         public string AssemblyVersion { get; set; }

+ 1 - 1
AppServer/Metadata/Dto/AuthDto.cs

@@ -3,7 +3,7 @@ using Quadarax.Foundation.Core.Data.Interface.Entity;
 
 namespace BO.AppServer.Metadata.Dto
 {
-    public class AuthDto : BoDto, IReadDto
+    public class AuthDto : Dto, IReadDto
     {
         [JsonPropertyName("ticket")]
         public string Ticket { get; set; }

+ 1 - 1
AppServer/Metadata/Dto/BillingPlanDto.cs

@@ -26,7 +26,7 @@ namespace BO.AppServer.Metadata.Dto
 
     #region *** BillingPlan - Create ***
 
-    public class BillingPlanCDto : IDto, ICreateDto
+    public class BillingPlanCDto : Dto, ICreateDto
     {
         public string Name { get; set; }
         public string Code { get; set; }

+ 17 - 5
AppServer/Metadata/Dto/BoAuditDto.cs

@@ -1,12 +1,24 @@
 using System;
+using System.Text;
 
 namespace BO.AppServer.Metadata.Dto
 {
-    public abstract class BoAuditDto : BoDto
+    public class BoAuditDto : Dto
     {
-        public DateTime Created { get; }
-        public DateTime Changed { get; }
-        public string CreatedBy { get; }
-        public string ModifiedBy { get; }
+        public DateTime Created { get; set; }
+        public DateTime? Changed { get; set; }
+        public string CreatedBy { get; set; }
+        public string ChangedBy { get; set; }
+
+
+        public override string ToString()
+        {
+            var sb = new StringBuilder();
+            sb.Append("Created: ").Append(Created).Append(" by ").Append(CreatedBy);
+            if (Changed.HasValue)
+                sb.Append("/Changed: ").Append(Changed.Value).Append(" by ").Append(ChangedBy);
+
+            return sb.ToString();
+        }
     }
 }

+ 1 - 2
AppServer/Metadata/Dto/BoDto.cs

@@ -1,9 +1,8 @@
 using System.Text.Json.Serialization;
-using Quadarax.Foundation.Core.Data.Interface.Entity;
 
 namespace BO.AppServer.Metadata.Dto
 {
-    public abstract class BoDto : IDto
+    public abstract class BoDto : Dto
     {
         [JsonPropertyName("id")]
         public long Id { get; set; }

+ 18 - 12
AppServer/Metadata/Dto/DocumentDto.cs

@@ -10,16 +10,22 @@ namespace BO.AppServer.Metadata.Dto
     public class DocumentBaseDto : BoDto
     {
         public string Name { get; }
-        public DocumentStatusEnum Status { get; }
+        public DocumentStatusEnum Status { get; set; }
     }
 
     #endregion
 
     #region *** Document - Create ***
 
-    public class DocumentCDto : IDto, ICreateDto
+    public class DocumentCDto : Dto, ICreateDto
     {
-        public string Name { get; }
+        public string Name { get; set; }
+
+        public DocumentCDto(){}
+        public DocumentCDto(string name)
+        {
+            Name = name;
+        }
     }
 
     #endregion
@@ -36,17 +42,17 @@ namespace BO.AppServer.Metadata.Dto
 
     public class DocumentRDto : DocumentBaseDto, IReadDto
     {
-        public DateTime? LastDownloaded { get; }
-        public BoAuditDto Audit { get; }
+        public DateTime? LastDownloaded { get; set; }
+        public BoAuditDto Audit { get; set;}
 
-        public int PresetCreditCost { get; }
-        public bool PresetWatermark { get; }
-        public int PresetQuality { get; }
-        public string PresetProcessProfile { get; }
-        public bool PresetTest { get; }
+        public int PresetCreditCost { get; set;}
+        public bool PresetWatermark { get; set;}
+        public int PresetQuality { get; set;}
+        public string PresetProcessProfile { get; set;}
+        public bool PresetTest { get; set;}
 
-        public IEnumerable<ArtifactRDto> Artifacts { get; }
-        public IEnumerable<DocumentStatusHistoryRDto> History { get; }
+        public IEnumerable<ArtifactRDto> Artifacts { get; set;}
+        public IEnumerable<DocumentStatusHistoryRDto> History { get; set;}
     }
 
     #endregion

+ 1 - 1
AppServer/Metadata/Dto/DocumentStatusHistoryDto.cs

@@ -14,7 +14,7 @@ namespace BO.AppServer.Metadata.Dto
 
     #region *** DocumentStatusHistory - Create ***
 
-    internal class DocumentStatusHistoryCDto : BoDto, ICreateDto
+    internal class DocumentStatusHistoryCDto : Dto, ICreateDto
     {
     }
 

+ 11 - 0
AppServer/Metadata/Dto/Dto.cs

@@ -0,0 +1,11 @@
+using Quadarax.Foundation.Core.Data.Interface.Entity;
+
+namespace BO.AppServer.Metadata.Dto
+{
+    public abstract class Dto : IDto
+    {
+        protected Dto()
+        {
+        }
+    }
+}

+ 1 - 1
AppServer/Metadata/Dto/MimeTypeDto.cs

@@ -16,7 +16,7 @@ namespace BO.AppServer.Metadata.Dto
 
     #region *** MimeType - Create ***
 
-    public class MimeTypeCDto : IDto, ICreateDto
+    public class MimeTypeCDto : Dto, ICreateDto
     {
         public string Mimetype1 { get; set; }
         public string Extensions { get; set; }

+ 2 - 4
AppServer/Metadata/Dto/PingDto.cs

@@ -1,8 +1,6 @@
-using Quadarax.Foundation.Core.Data.Interface.Entity;
-
-namespace BO.AppServer.Metadata.Dto
+namespace BO.AppServer.Metadata.Dto
 {
-    public class PingDto : IDto
+    public class PingDto : Dto
     {
         public string Status { get; set; }
     }

+ 1 - 2
AppServer/Metadata/Dto/StatisticsDto.cs

@@ -1,9 +1,8 @@
 using System;
-using Quadarax.Foundation.Core.Data.Interface.Entity;
 
 namespace BO.AppServer.Metadata.Dto
 {
-    public class StatisticsDto : IDto
+    public class StatisticsDto : Dto
     {
         public int Year { get; set; }
         public int Month { get; set; }

+ 52 - 0
AppServer/Metadata/Dto/StructureDto.cs

@@ -0,0 +1,52 @@
+using Quadarax.Foundation.Core.Data.Interface.Entity;
+
+namespace BO.AppServer.Metadata.Dto
+{
+    #region *** Structure - Base ***
+
+    public class StructureBaseDto : BoDto
+    {
+        public string Name { get; set; }
+        public long? ParentId { get; set; }
+    }
+
+    #endregion
+
+    #region *** Structure - Create ***
+
+    internal class StructureCDto : StructureBaseDto, ICreateDto
+    {
+    }
+
+    #endregion
+
+    #region *** Structure - Update ***
+
+    internal class StructureUDto : StructureBaseDto, IUpdateDto
+    {
+    }
+
+    #endregion
+
+    #region *** Structure - Read ***
+
+    public class StructureRDto : StructureBaseDto, IReadDto
+    {
+        public BoAuditDto Audit { get; set; }        
+        public int DocumentAllCount { get; set; }
+        public int DocumentPendingCount { get; set; }
+        public int DocumentWorkingCount { get; set; }
+        public int DocumentDoneCount { get; set; }
+        public int DocumentFailCount { get; set; }
+    }
+
+    #endregion
+
+    #region *** Structure - Delete ***
+
+    internal class StructureDDto : BoDto, IDeleteDto
+    {
+    }
+
+    #endregion
+}

+ 1 - 1
AppServer/Metadata/Dto/UserDto.cs

@@ -4,7 +4,7 @@ using Quadarax.Foundation.Core.Data.Interface.Entity;
 namespace BO.AppServer.Metadata.Dto
 {
     #region *** User - Create ***
-    public class UserCDto : IDto, ICreateDto
+    public class UserCDto : Dto, ICreateDto
     {
         public string Name { get; set; }
         public string Password { get; set; }

+ 7 - 3
AppServer/Metadata/Dto/WorkspaceDto.cs

@@ -17,9 +17,12 @@ namespace BO.AppServer.Metadata.Dto
 
     #region *** Workspace - Create ***
 
-    public class WorkspaceCDto : WorkspaceBaseDto, ICreateDto
+    public class WorkspaceCDto : Dto, ICreateDto
     {
         public string AssignedUserName { get; set; }
+        public string Name { get; set; }
+        public string Apipassword { get; set; }
+        public string BillingPlanCode { get; set; }
     }
 
     #endregion
@@ -38,10 +41,11 @@ namespace BO.AppServer.Metadata.Dto
     public class WorkspaceRDto : WorkspaceBaseDto, IReadDto
     {
         public Guid Apikey { get; set; }
-        public long CreditAmount { get; set; }
+        public decimal CreditAmount { get; set; }
         public IEnumerable<BillingInfoRDto> BillingInfos { get; set; }
         public int DocumentsCount { get; set; }
-        public BoAuditDto Audit { get; }
+        public BoAuditDto Audit { get; set; }
+        public IEnumerable<StructureRDto> Structures { get; set; }
     }
 
     #endregion

+ 27 - 0
AppServer/Metadata/Extensions/ResultValueExt.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Linq;
+using Quadarax.Foundation.Core.Data.Interface.Entity;
+using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;
+
+namespace BO.AppServer.Metadata.Extensions
+{
+    public static class ResultValueExt
+    {
+
+        public static bool HasNoDataException<TDto>(this ResultValueDto<TDto> result) where TDto : IDto, new()
+        {
+            if (result == null)
+                throw new ArgumentNullException(nameof(result));
+
+            return result.Errors.Any(x => x.Code == 1099);
+        }
+
+        public static bool HasNoDataException<TDto>(this ResultsValueDto<TDto> result) where TDto : IDto, new()
+        {
+            if (result == null)
+                throw new ArgumentNullException(nameof(result));
+
+            return result.Errors.Any(x => x.Code == 1099);
+        }
+    }
+}

+ 25 - 2
AppServer/Web/Services/ConsoleController.cs

@@ -140,6 +140,14 @@ namespace BO.AppServer.Web.Services
             return await Call(async () => await _srvWorkspace.GetWorkspacesAsync(scope, new HeaderPagingDto(page, pageSize), context));
         }
 
+        [HttpGet("{ticket}/workspace/{identificationType}/{identificationValue}")]
+        public async Task<ResultValueDto<WorkspaceRDto>> GetWorkspace(string ticket, IdentificationTypeEnum identificationType, string identificationValue)
+        {
+            CheckAccess(ticket);
+            return await Call(async () => await _srvWorkspace.GetWorkspace(identificationType, identificationValue));
+        }
+
+
         [HttpPost("{ticket}/workspace")]
         public async Task<ResultValueDto<WorkspaceRDto>> CreateWorkspace(string ticket, [FromBody] WorkspaceCDto workspace)
         {
@@ -169,7 +177,7 @@ namespace BO.AppServer.Web.Services
         }
 
         [HttpGet("{Ticket}/workspace/{wrkspApiKey}/document/{documentId}")]
-        public async Task<ResultValueDto<DocumentRDto>> GetAllWorkspaceDocument(string ticket, string wrkspApiKey,long documentId,[FromHeader] string apiKeyPassword,  [FromHeader] string context)
+        public async Task<ResultValueDto<DocumentRDto>> GetWorkspaceDocument(string ticket, string wrkspApiKey,long documentId,[FromHeader] string apiKeyPassword,  [FromHeader] string context)
         {
             CheckAccess(ticket);
             return await Call(async () =>
@@ -179,6 +187,17 @@ namespace BO.AppServer.Web.Services
             });
         }
 
+        [HttpGet("{Ticket}/workspace/{wrkspApiKey}/document")]
+        public async Task<ResultValueDto<DocumentRDto>> GetWorkspaceDocumentByName(string ticket, string wrkspApiKey,[FromHeader]string documentName,[FromHeader] string apiKeyPassword,  [FromHeader] string context)
+        {
+            CheckAccess(ticket);
+            return await Call(async () =>
+            {
+                var id = await _srvWorkspace.GetWorkspaceIdByAPIAccess(wrkspApiKey, apiKeyPassword, context);
+                return await _srvDocument.GetDocumentByNameAsync(id, documentName);
+            });
+        }
+
         [HttpGet("{Ticket}/workspace/{wrkspApiKey}/document/{documentId}/artifact/{artifactId}/content")]
         public async Task<IActionResult> GetWorkspaceDocumentArtifactContent(string ticket, string wrkspApiKey,long documentId,long artifactId,[FromHeader] string apiKeyPassword,  [FromHeader] string context)
         {
@@ -209,12 +228,16 @@ namespace BO.AppServer.Web.Services
         }
 
         [HttpPost("{Ticket}/workspace/{wrkspApiKey}/document/{documentId}/artifact/input")]
-        public async Task<ResultValueDto<ArtifactRDto>> AppendWorkspaceDocumentArtifact(string ticket, string wrkspApiKey, long documentId,[FromHeader] string apiKeyPassword, [FromHeader] string context, [FromBody] ArtifactCDto artifact, IFormFile content)
+        [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)
         {
             CheckAccess(ticket);
             return await Call(async () =>
             {
                 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);
             });

+ 4 - 1
AppServer/Web/Startup.cs

@@ -1,7 +1,6 @@
 using System;
 using System.IO.Abstractions;
 using System.Security.Principal;
-using System.Threading.Tasks;
 using BO.AppServer.Business.Configuration;
 using BO.AppServer.Business.Services;
 using Microsoft.AspNetCore.Authorization;
@@ -66,6 +65,7 @@ namespace AppServer
             services.AddScoped<CatalogueService>();
             services.AddScoped<WorkspaceService>();
             services.AddScoped<StorageService>();
+            services.AddScoped<DocumentService>();
 
             // Bo Repos
             services.AddScoped<DataDomain, BOContext>();
@@ -73,6 +73,8 @@ namespace AppServer
             services.AddScoped<IUnitOfWork<DataDomain>, UnitOfWorkDataDomain>();
             services.AddScoped<UserRepo>();
             services.AddScoped<WorkspaceRepo>();
+            services.AddScoped<WorkspaceBillingRepo>();
+            services.AddScoped<StructureRepo>();
             services.AddScoped<BillingPlanRepo>();
             services.AddScoped<MimeTypeRepo>();
             services.AddScoped<StatisticRepo>();
@@ -154,6 +156,7 @@ namespace AppServer
             {
                 _srvConfiguration.InstallDev();
             }
+            _srvConfiguration.LogInfo("Application server is fully initialized.");
         }
     }
 }

+ 1 - 1
AppServer/Web/appsettings.Development.json

@@ -6,7 +6,7 @@
       // Enable components logging
       "Microsoft.Hosting.Lifetime": "Information",
       // Enable EF logging
-      // "Microsoft.EntityFrameworkCore.Database.Command": "Information"
+      "Microsoft.EntityFrameworkCore.Database.Command": "Information"
     }
   },
   "Bo": {

+ 19 - 0
Common/qdr.fnd.core.data/Extensions/QueryableExt.cs

@@ -0,0 +1,19 @@
+using System.Linq;
+using Microsoft.EntityFrameworkCore;
+
+namespace Quadarax.Foundation.Core.Data.Extensions
+{
+    public static class QueryableExt
+    {
+        public static IQueryable<TEntity> MultiInclude<TEntity>(this IQueryable<TEntity> queryable, string[] includes) where TEntity : class
+        {
+            if (includes.Length == 0)
+                return queryable;
+
+            foreach (var include in includes)
+                queryable = queryable.Include(include);
+            
+            return queryable;
+        }
+    }
+}

+ 13 - 0
Common/qdr.fnd.core.data/Mapper/Mapper.cs

@@ -41,6 +41,19 @@ namespace Quadarax.Foundation.Core.Data.Mapper
                 .Compile();
         }
 
+        protected void Define<TDao, TDto>(Action<LoomBuilder<TDao, TDto>> customFromDao)
+            where TDao : class, IDao<long>, new()
+            where TDto : class, IDto, new()
+        {
+            // from DAO to DTo
+            var builder = Define<TDao, TDto>();
+            builder.PublicProperties();
+            customFromDao?.Invoke(builder);
+            builder.Compile();
+
+        }
+
+
         #endregion
     }
 }

+ 8 - 4
Common/qdr.fnd.core.data/Repository/EntityRepository.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+
 using Microsoft.EntityFrameworkCore;
 using Quadarax.Foundation.Core.Data.Domain;
 using Quadarax.Foundation.Core.Data.Entity;
@@ -19,26 +20,29 @@ namespace Quadarax.Foundation.Core.Data.Repository
         private DbSet<TEntity> DbSetInstance => _unitOfWork.Context.GetDbSet<DbSet<TEntity>>();
         protected DataDomain Context => _unitOfWork.Context;
         
+        protected IList<string> DefaultIncludes { get; }
+
         protected EntityRepository(IUnitOfWork<DataDomain> unitOfWork)
         {
             _unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
+            DefaultIncludes = new List<string>();
             //_dbSet = domain.GetDbSet<DbSet<TEntity>>();
         }
 
         public virtual IQueryable<TEntity> Query(IPaging paging,bool usingUncommited=false)
         {
-            var local = DbSetInstance.Local.Page(paging.IsDisabled ? (int?) null : paging.Page, paging.PageSize);
+            var local = DbSetInstance.Local.Page(paging.IsDisabled ? (int?) null : paging.Page, paging.PageSize).AsQueryable();
             if (local.Any() && usingUncommited)
             {
-                return local.AsQueryable();
+                return local.MultiInclude(DefaultIncludes.ToArray());
             }
 
-            return DbSetInstance.Page(paging.IsDisabled ? (int?) null : paging.Page, paging.PageSize);
+            return DbSetInstance.Page(paging.IsDisabled ? (int?) null : paging.Page, paging.PageSize).MultiInclude(DefaultIncludes.ToArray());
         }
 
         public virtual TEntity Get(TKey id)
         {
-            return DbSetInstance.Find(id);
+            return Query(new Paging()).MultiInclude(DefaultIncludes.ToArray()).FirstOrDefault(x => Equals(x.Id, id));
         }
 
         public virtual IQueryable<TEntity> Get(IEnumerable<TKey> ids)

+ 593 - 0
Common/qdr.fnd.core/IO/MimeTypes/MimeTypeUtil.cs

@@ -0,0 +1,593 @@
+using System;
+using System.Collections.Generic;
+// ReSharper disable StringLiteralTypo
+
+
+namespace Quadarax.Foundation.Core.IO.MimeTypes
+{
+    public class MimeTypeUtil
+    {
+        private static readonly IDictionary<string, string> Mappings =
+            new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase)
+            {
+
+                #region *** Mapping ***
+
+                { ".323", "text/h323" },
+                { ".3g2", "video/3gpp2" },
+                { ".3gp", "video/3gpp" },
+                { ".3gp2", "video/3gpp2" },
+                { ".3gpp", "video/3gpp" },
+                { ".7z", "application/x-7z-compressed" },
+                { ".aa", "audio/audible" },
+                { ".AAC", "audio/aac" },
+                { ".aaf", "application/octet-stream" },
+                { ".aax", "audio/vnd.audible.aax" },
+                { ".ac3", "audio/ac3" },
+                { ".aca", "application/octet-stream" },
+                { ".accda", "application/msaccess.addin" },
+                { ".accdb", "application/msaccess" },
+                { ".accdc", "application/msaccess.cab" },
+                { ".accde", "application/msaccess" },
+                { ".accdr", "application/msaccess.runtime" },
+                { ".accdt", "application/msaccess" },
+                { ".accdw", "application/msaccess.webapplication" },
+                { ".accft", "application/msaccess.ftemplate" },
+                { ".acx", "application/internet-property-stream" },
+                { ".AddIn", "text/xml" },
+                { ".ade", "application/msaccess" },
+                { ".adobebridge", "application/x-bridge-url" },
+                { ".adp", "application/msaccess" },
+                { ".ADT", "audio/vnd.dlna.adts" },
+                { ".ADTS", "audio/aac" },
+                { ".afm", "application/octet-stream" },
+                { ".ai", "application/postscript" },
+                { ".aif", "audio/x-aiff" },
+                { ".aifc", "audio/aiff" },
+                { ".aiff", "audio/aiff" },
+                { ".air", "application/vnd.adobe.air-application-installer-package+zip" },
+                { ".amc", "application/x-mpeg" },
+                { ".application", "application/x-ms-application" },
+                { ".art", "image/x-jg" },
+                { ".asa", "application/xml" },
+                { ".asax", "application/xml" },
+                { ".ascx", "application/xml" },
+                { ".asd", "application/octet-stream" },
+                { ".asf", "video/x-ms-asf" },
+                { ".ashx", "application/xml" },
+                { ".asi", "application/octet-stream" },
+                { ".asm", "text/plain" },
+                { ".asmx", "application/xml" },
+                { ".aspx", "application/xml" },
+                { ".asr", "video/x-ms-asf" },
+                { ".asx", "video/x-ms-asf" },
+                { ".atom", "application/atom+xml" },
+                { ".au", "audio/basic" },
+                { ".avi", "video/x-msvideo" },
+                { ".axs", "application/olescript" },
+                { ".bas", "text/plain" },
+                { ".bcpio", "application/x-bcpio" },
+                { ".bin", "application/octet-stream" },
+                { ".bmp", "image/bmp" },
+                { ".c", "text/plain" },
+                { ".cab", "application/octet-stream" },
+                { ".caf", "audio/x-caf" },
+                { ".calx", "application/vnd.ms-office.calx" },
+                { ".cat", "application/vnd.ms-pki.seccat" },
+                { ".cc", "text/plain" },
+                { ".cd", "text/plain" },
+                { ".cdda", "audio/aiff" },
+                { ".cdf", "application/x-cdf" },
+                { ".cer", "application/x-x509-ca-cert" },
+                { ".chm", "application/octet-stream" },
+                { ".class", "application/x-java-applet" },
+                { ".clp", "application/x-msclip" },
+                { ".cmx", "image/x-cmx" },
+                { ".cnf", "text/plain" },
+                { ".cod", "image/cis-cod" },
+                { ".config", "application/xml" },
+                { ".contact", "text/x-ms-contact" },
+                { ".coverage", "application/xml" },
+                { ".cpio", "application/x-cpio" },
+                { ".cpp", "text/plain" },
+                { ".crd", "application/x-mscardfile" },
+                { ".crl", "application/pkix-crl" },
+                { ".crt", "application/x-x509-ca-cert" },
+                { ".cs", "text/plain" },
+                { ".csdproj", "text/plain" },
+                { ".csh", "application/x-csh" },
+                { ".csproj", "text/plain" },
+                { ".css", "text/css" },
+                { ".csv", "text/csv" },
+                { ".cur", "application/octet-stream" },
+                { ".cxx", "text/plain" },
+                { ".dat", "application/octet-stream" },
+                { ".datasource", "application/xml" },
+                { ".dbproj", "text/plain" },
+                { ".dcr", "application/x-director" },
+                { ".def", "text/plain" },
+                { ".deploy", "application/octet-stream" },
+                { ".der", "application/x-x509-ca-cert" },
+                { ".dgml", "application/xml" },
+                { ".dib", "image/bmp" },
+                { ".dif", "video/x-dv" },
+                { ".dir", "application/x-director" },
+                { ".disco", "text/xml" },
+                { ".dll", "application/x-msdownload" },
+                { ".dll.config", "text/xml" },
+                { ".dlm", "text/dlm" },
+                { ".doc", "application/msword" },
+                { ".docm", "application/vnd.ms-word.document.macroEnabled.12" },
+                { ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
+                { ".dot", "application/msword" },
+                { ".dotm", "application/vnd.ms-word.template.macroEnabled.12" },
+                { ".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template" },
+                { ".dsp", "application/octet-stream" },
+                { ".dsw", "text/plain" },
+                { ".dtd", "text/xml" },
+                { ".dtsConfig", "text/xml" },
+                { ".dv", "video/x-dv" },
+                { ".dvi", "application/x-dvi" },
+                { ".dwf", "drawing/x-dwf" },
+                { ".dwp", "application/octet-stream" },
+                { ".dxr", "application/x-director" },
+                { ".eml", "message/rfc822" },
+                { ".emz", "application/octet-stream" },
+                { ".eot", "application/octet-stream" },
+                { ".eps", "application/postscript" },
+                { ".etl", "application/etl" },
+                { ".etx", "text/x-setext" },
+                { ".evy", "application/envoy" },
+                { ".exe", "application/octet-stream" },
+                { ".exe.config", "text/xml" },
+                { ".fdf", "application/vnd.fdf" },
+                { ".fif", "application/fractals" },
+                { ".filters", "Application/xml" },
+                { ".fla", "application/octet-stream" },
+                { ".flr", "x-world/x-vrml" },
+                { ".flv", "video/x-flv" },
+                { ".fsscript", "application/fsharp-script" },
+                { ".fsx", "application/fsharp-script" },
+                { ".generictest", "application/xml" },
+                { ".gif", "image/gif" },
+                { ".group", "text/x-ms-group" },
+                { ".gsm", "audio/x-gsm" },
+                { ".gtar", "application/x-gtar" },
+                { ".gz", "application/x-gzip" },
+                { ".h", "text/plain" },
+                { ".hdf", "application/x-hdf" },
+                { ".hdml", "text/x-hdml" },
+                { ".hhc", "application/x-oleobject" },
+                { ".hhk", "application/octet-stream" },
+                { ".hhp", "application/octet-stream" },
+                { ".hlp", "application/winhlp" },
+                { ".hpp", "text/plain" },
+                { ".hqx", "application/mac-binhex40" },
+                { ".hta", "application/hta" },
+                { ".htc", "text/x-component" },
+                { ".htm", "text/html" },
+                { ".html", "text/html" },
+                { ".htt", "text/webviewhtml" },
+                { ".hxa", "application/xml" },
+                { ".hxc", "application/xml" },
+                { ".hxd", "application/octet-stream" },
+                { ".hxe", "application/xml" },
+                { ".hxf", "application/xml" },
+                { ".hxh", "application/octet-stream" },
+                { ".hxi", "application/octet-stream" },
+                { ".hxk", "application/xml" },
+                { ".hxq", "application/octet-stream" },
+                { ".hxr", "application/octet-stream" },
+                { ".hxs", "application/octet-stream" },
+                { ".hxt", "text/html" },
+                { ".hxv", "application/xml" },
+                { ".hxw", "application/octet-stream" },
+                { ".hxx", "text/plain" },
+                { ".i", "text/plain" },
+                { ".ico", "image/x-icon" },
+                { ".ics", "application/octet-stream" },
+                { ".idl", "text/plain" },
+                { ".ief", "image/ief" },
+                { ".iii", "application/x-iphone" },
+                { ".inc", "text/plain" },
+                { ".inf", "application/octet-stream" },
+                { ".inl", "text/plain" },
+                { ".ins", "application/x-internet-signup" },
+                { ".ipa", "application/x-itunes-ipa" },
+                { ".ipg", "application/x-itunes-ipg" },
+                { ".ipproj", "text/plain" },
+                { ".ipsw", "application/x-itunes-ipsw" },
+                { ".iqy", "text/x-ms-iqy" },
+                { ".isp", "application/x-internet-signup" },
+                { ".ite", "application/x-itunes-ite" },
+                { ".itlp", "application/x-itunes-itlp" },
+                { ".itms", "application/x-itunes-itms" },
+                { ".itpc", "application/x-itunes-itpc" },
+                { ".IVF", "video/x-ivf" },
+                { ".jar", "application/java-archive" },
+                { ".java", "application/octet-stream" },
+                { ".jck", "application/liquidmotion" },
+                { ".jcz", "application/liquidmotion" },
+                { ".jfif", "image/pjpeg" },
+                { ".jnlp", "application/x-java-jnlp-file" },
+                { ".jpb", "application/octet-stream" },
+                { ".jpe", "image/jpeg" },
+                { ".jpeg", "image/jpeg" },
+                { ".jpg", "image/jpeg" },
+                { ".js", "application/x-javascript" },
+                { ".json", "application/json" },
+                { ".jsx", "text/jscript" },
+                { ".jsxbin", "text/plain" },
+                { ".latex", "application/x-latex" },
+                { ".library-ms", "application/windows-library+xml" },
+                { ".lit", "application/x-ms-reader" },
+                { ".loadtest", "application/xml" },
+                { ".lpk", "application/octet-stream" },
+                { ".lsf", "video/x-la-asf" },
+                { ".lst", "text/plain" },
+                { ".lsx", "video/x-la-asf" },
+                { ".lzh", "application/octet-stream" },
+                { ".m13", "application/x-msmediaview" },
+                { ".m14", "application/x-msmediaview" },
+                { ".m1v", "video/mpeg" },
+                { ".m2t", "video/vnd.dlna.mpeg-tts" },
+                { ".m2ts", "video/vnd.dlna.mpeg-tts" },
+                { ".m2v", "video/mpeg" },
+                { ".m3u", "audio/x-mpegurl" },
+                { ".m3u8", "audio/x-mpegurl" },
+                { ".m4a", "audio/m4a" },
+                { ".m4b", "audio/m4b" },
+                { ".m4p", "audio/m4p" },
+                { ".m4r", "audio/x-m4r" },
+                { ".m4v", "video/x-m4v" },
+                { ".mac", "image/x-macpaint" },
+                { ".mak", "text/plain" },
+                { ".man", "application/x-troff-man" },
+                { ".manifest", "application/x-ms-manifest" },
+                { ".map", "text/plain" },
+                { ".master", "application/xml" },
+                { ".mda", "application/msaccess" },
+                { ".mdb", "application/x-msaccess" },
+                { ".mde", "application/msaccess" },
+                { ".mdp", "application/octet-stream" },
+                { ".me", "application/x-troff-me" },
+                { ".mfp", "application/x-shockwave-flash" },
+                { ".mht", "message/rfc822" },
+                { ".mhtml", "message/rfc822" },
+                { ".mid", "audio/mid" },
+                { ".midi", "audio/mid" },
+                { ".mix", "application/octet-stream" },
+                { ".mk", "text/plain" },
+                { ".mmf", "application/x-smaf" },
+                { ".mno", "text/xml" },
+                { ".mny", "application/x-msmoney" },
+                { ".mod", "video/mpeg" },
+                { ".mov", "video/quicktime" },
+                { ".movie", "video/x-sgi-movie" },
+                { ".mp2", "video/mpeg" },
+                { ".mp2v", "video/mpeg" },
+                { ".mp3", "audio/mpeg" },
+                { ".mp4", "video/mp4" },
+                { ".mp4v", "video/mp4" },
+                { ".mpa", "video/mpeg" },
+                { ".mpe", "video/mpeg" },
+                { ".mpeg", "video/mpeg" },
+                { ".mpf", "application/vnd.ms-mediapackage" },
+                { ".mpg", "video/mpeg" },
+                { ".mpp", "application/vnd.ms-project" },
+                { ".mpv2", "video/mpeg" },
+                { ".mqv", "video/quicktime" },
+                { ".ms", "application/x-troff-ms" },
+                { ".msi", "application/octet-stream" },
+                { ".mso", "application/octet-stream" },
+                { ".mts", "video/vnd.dlna.mpeg-tts" },
+                { ".mtx", "application/xml" },
+                { ".mvb", "application/x-msmediaview" },
+                { ".mvc", "application/x-miva-compiled" },
+                { ".mxp", "application/x-mmxp" },
+                { ".nc", "application/x-netcdf" },
+                { ".nsc", "video/x-ms-asf" },
+                { ".nws", "message/rfc822" },
+                { ".ocx", "application/octet-stream" },
+                { ".oda", "application/oda" },
+                { ".odc", "text/x-ms-odc" },
+                { ".odh", "text/plain" },
+                { ".odl", "text/plain" },
+                { ".odp", "application/vnd.oasis.opendocument.presentation" },
+                { ".ods", "application/oleobject" },
+                { ".odt", "application/vnd.oasis.opendocument.text" },
+                { ".one", "application/onenote" },
+                { ".onea", "application/onenote" },
+                { ".onepkg", "application/onenote" },
+                { ".onetmp", "application/onenote" },
+                { ".onetoc", "application/onenote" },
+                { ".onetoc2", "application/onenote" },
+                { ".orderedtest", "application/xml" },
+                { ".osdx", "application/opensearchdescription+xml" },
+                { ".p10", "application/pkcs10" },
+                { ".p12", "application/x-pkcs12" },
+                { ".p7b", "application/x-pkcs7-certificates" },
+                { ".p7c", "application/pkcs7-mime" },
+                { ".p7m", "application/pkcs7-mime" },
+                { ".p7r", "application/x-pkcs7-certreqresp" },
+                { ".p7s", "application/pkcs7-signature" },
+                { ".pbm", "image/x-portable-bitmap" },
+                { ".pcast", "application/x-podcast" },
+                { ".pct", "image/pict" },
+                { ".pcx", "application/octet-stream" },
+                { ".pcz", "application/octet-stream" },
+                { ".pdf", "application/pdf" },
+                { ".pfb", "application/octet-stream" },
+                { ".pfm", "application/octet-stream" },
+                { ".pfx", "application/x-pkcs12" },
+                { ".pgm", "image/x-portable-graymap" },
+                { ".pic", "image/pict" },
+                { ".pict", "image/pict" },
+                { ".pkgdef", "text/plain" },
+                { ".pkgundef", "text/plain" },
+                { ".pko", "application/vnd.ms-pki.pko" },
+                { ".pls", "audio/scpls" },
+                { ".pma", "application/x-perfmon" },
+                { ".pmc", "application/x-perfmon" },
+                { ".pml", "application/x-perfmon" },
+                { ".pmr", "application/x-perfmon" },
+                { ".pmw", "application/x-perfmon" },
+                { ".png", "image/png" },
+                { ".pnm", "image/x-portable-anymap" },
+                { ".pnt", "image/x-macpaint" },
+                { ".pntg", "image/x-macpaint" },
+                { ".pnz", "image/png" },
+                { ".pot", "application/vnd.ms-powerpoint" },
+                { ".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12" },
+                { ".potx", "application/vnd.openxmlformats-officedocument.presentationml.template" },
+                { ".ppa", "application/vnd.ms-powerpoint" },
+                { ".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12" },
+                { ".ppm", "image/x-portable-pixmap" },
+                { ".pps", "application/vnd.ms-powerpoint" },
+                { ".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12" },
+                { ".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow" },
+                { ".ppt", "application/vnd.ms-powerpoint" },
+                { ".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12" },
+                { ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" },
+                { ".prf", "application/pics-rules" },
+                { ".prm", "application/octet-stream" },
+                { ".prx", "application/octet-stream" },
+                { ".ps", "application/postscript" },
+                { ".psc1", "application/PowerShell" },
+                { ".psd", "application/octet-stream" },
+                { ".psess", "application/xml" },
+                { ".psm", "application/octet-stream" },
+                { ".psp", "application/octet-stream" },
+                { ".pub", "application/x-mspublisher" },
+                { ".pwz", "application/vnd.ms-powerpoint" },
+                { ".qht", "text/x-html-insertion" },
+                { ".qhtm", "text/x-html-insertion" },
+                { ".qt", "video/quicktime" },
+                { ".qti", "image/x-quicktime" },
+                { ".qtif", "image/x-quicktime" },
+                { ".qtl", "application/x-quicktimeplayer" },
+                { ".qxd", "application/octet-stream" },
+                { ".ra", "audio/x-pn-realaudio" },
+                { ".ram", "audio/x-pn-realaudio" },
+                { ".rar", "application/octet-stream" },
+                { ".ras", "image/x-cmu-raster" },
+                { ".rat", "application/rat-file" },
+                { ".rc", "text/plain" },
+                { ".rc2", "text/plain" },
+                { ".rct", "text/plain" },
+                { ".rdlc", "application/xml" },
+                { ".resx", "application/xml" },
+                { ".rf", "image/vnd.rn-realflash" },
+                { ".rgb", "image/x-rgb" },
+                { ".rgs", "text/plain" },
+                { ".rm", "application/vnd.rn-realmedia" },
+                { ".rmi", "audio/mid" },
+                { ".rmp", "application/vnd.rn-rn_music_package" },
+                { ".roff", "application/x-troff" },
+                { ".rpm", "audio/x-pn-realaudio-plugin" },
+                { ".rqy", "text/x-ms-rqy" },
+                { ".rtf", "application/rtf" },
+                { ".rtx", "text/richtext" },
+                { ".ruleset", "application/xml" },
+                { ".s", "text/plain" },
+                { ".safariextz", "application/x-safari-safariextz" },
+                { ".scd", "application/x-msschedule" },
+                { ".sct", "text/scriptlet" },
+                { ".sd2", "audio/x-sd2" },
+                { ".sdp", "application/sdp" },
+                { ".sea", "application/octet-stream" },
+                { ".searchConnector-ms", "application/windows-search-connector+xml" },
+                { ".setpay", "application/set-payment-initiation" },
+                { ".setreg", "application/set-registration-initiation" },
+                { ".settings", "application/xml" },
+                { ".sgimb", "application/x-sgimb" },
+                { ".sgml", "text/sgml" },
+                { ".sh", "application/x-sh" },
+                { ".shar", "application/x-shar" },
+                { ".shtml", "text/html" },
+                { ".sit", "application/x-stuffit" },
+                { ".sitemap", "application/xml" },
+                { ".skin", "application/xml" },
+                { ".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12" },
+                { ".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide" },
+                { ".slk", "application/vnd.ms-excel" },
+                { ".sln", "text/plain" },
+                { ".slupkg-ms", "application/x-ms-license" },
+                { ".smd", "audio/x-smd" },
+                { ".smi", "application/octet-stream" },
+                { ".smx", "audio/x-smd" },
+                { ".smz", "audio/x-smd" },
+                { ".snd", "audio/basic" },
+                { ".snippet", "application/xml" },
+                { ".snp", "application/octet-stream" },
+                { ".sol", "text/plain" },
+                { ".sor", "text/plain" },
+                { ".spc", "application/x-pkcs7-certificates" },
+                { ".spl", "application/futuresplash" },
+                { ".src", "application/x-wais-source" },
+                { ".srf", "text/plain" },
+                { ".SSISDeploymentManifest", "text/xml" },
+                { ".ssm", "application/streamingmedia" },
+                { ".sst", "application/vnd.ms-pki.certstore" },
+                { ".stl", "application/vnd.ms-pki.stl" },
+                { ".sv4cpio", "application/x-sv4cpio" },
+                { ".sv4crc", "application/x-sv4crc" },
+                { ".svc", "application/xml" },
+                { ".swf", "application/x-shockwave-flash" },
+                { ".t", "application/x-troff" },
+                { ".tar", "application/x-tar" },
+                { ".tcl", "application/x-tcl" },
+                { ".testrunconfig", "application/xml" },
+                { ".testsettings", "application/xml" },
+                { ".tex", "application/x-tex" },
+                { ".texi", "application/x-texinfo" },
+                { ".texinfo", "application/x-texinfo" },
+                { ".tgz", "application/x-compressed" },
+                { ".thmx", "application/vnd.ms-officetheme" },
+                { ".thn", "application/octet-stream" },
+                { ".tif", "image/tiff" },
+                { ".tiff", "image/tiff" },
+                { ".tlh", "text/plain" },
+                { ".tli", "text/plain" },
+                { ".toc", "application/octet-stream" },
+                { ".tr", "application/x-troff" },
+                { ".trm", "application/x-msterminal" },
+                { ".trx", "application/xml" },
+                { ".ts", "video/vnd.dlna.mpeg-tts" },
+                { ".tsv", "text/tab-separated-values" },
+                { ".ttf", "application/octet-stream" },
+                { ".tts", "video/vnd.dlna.mpeg-tts" },
+                { ".txt", "text/plain" },
+                { ".u32", "application/octet-stream" },
+                { ".uls", "text/iuls" },
+                { ".user", "text/plain" },
+                { ".ustar", "application/x-ustar" },
+                { ".vb", "text/plain" },
+                { ".vbdproj", "text/plain" },
+                { ".vbk", "video/mpeg" },
+                { ".vbproj", "text/plain" },
+                { ".vbs", "text/vbscript" },
+                { ".vcf", "text/x-vcard" },
+                { ".vcproj", "Application/xml" },
+                { ".vcs", "text/plain" },
+                { ".vcxproj", "Application/xml" },
+                { ".vddproj", "text/plain" },
+                { ".vdp", "text/plain" },
+                { ".vdproj", "text/plain" },
+                { ".vdx", "application/vnd.ms-visio.viewer" },
+                { ".vml", "text/xml" },
+                { ".vscontent", "application/xml" },
+                { ".vsct", "text/xml" },
+                { ".vsd", "application/vnd.visio" },
+                { ".vsi", "application/ms-vsi" },
+                { ".vsix", "application/vsix" },
+                { ".vsixlangpack", "text/xml" },
+                { ".vsixmanifest", "text/xml" },
+                { ".vsmdi", "application/xml" },
+                { ".vspscc", "text/plain" },
+                { ".vss", "application/vnd.visio" },
+                { ".vsscc", "text/plain" },
+                { ".vssettings", "text/xml" },
+                { ".vssscc", "text/plain" },
+                { ".vst", "application/vnd.visio" },
+                { ".vstemplate", "text/xml" },
+                { ".vsto", "application/x-ms-vsto" },
+                { ".vsw", "application/vnd.visio" },
+                { ".vsx", "application/vnd.visio" },
+                { ".vtx", "application/vnd.visio" },
+                { ".wav", "audio/wav" },
+                { ".wave", "audio/wav" },
+                { ".wax", "audio/x-ms-wax" },
+                { ".wbk", "application/msword" },
+                { ".wbmp", "image/vnd.wap.wbmp" },
+                { ".wcm", "application/vnd.ms-works" },
+                { ".wdb", "application/vnd.ms-works" },
+                { ".wdp", "image/vnd.ms-photo" },
+                { ".webarchive", "application/x-safari-webarchive" },
+                { ".webtest", "application/xml" },
+                { ".wiq", "application/xml" },
+                { ".wiz", "application/msword" },
+                { ".wks", "application/vnd.ms-works" },
+                { ".WLMP", "application/wlmoviemaker" },
+                { ".wlpginstall", "application/x-wlpg-detect" },
+                { ".wlpginstall3", "application/x-wlpg3-detect" },
+                { ".wm", "video/x-ms-wm" },
+                { ".wma", "audio/x-ms-wma" },
+                { ".wmd", "application/x-ms-wmd" },
+                { ".wmf", "application/x-msmetafile" },
+                { ".wml", "text/vnd.wap.wml" },
+                { ".wmlc", "application/vnd.wap.wmlc" },
+                { ".wmls", "text/vnd.wap.wmlscript" },
+                { ".wmlsc", "application/vnd.wap.wmlscriptc" },
+                { ".wmp", "video/x-ms-wmp" },
+                { ".wmv", "video/x-ms-wmv" },
+                { ".wmx", "video/x-ms-wmx" },
+                { ".wmz", "application/x-ms-wmz" },
+                { ".wpl", "application/vnd.ms-wpl" },
+                { ".wps", "application/vnd.ms-works" },
+                { ".wri", "application/x-mswrite" },
+                { ".wrl", "x-world/x-vrml" },
+                { ".wrz", "x-world/x-vrml" },
+                { ".wsc", "text/scriptlet" },
+                { ".wsdl", "text/xml" },
+                { ".wvx", "video/x-ms-wvx" },
+                { ".x", "application/directx" },
+                { ".xaf", "x-world/x-vrml" },
+                { ".xaml", "application/xaml+xml" },
+                { ".xap", "application/x-silverlight-app" },
+                { ".xbap", "application/x-ms-xbap" },
+                { ".xbm", "image/x-xbitmap" },
+                { ".xdr", "text/plain" },
+                { ".xht", "application/xhtml+xml" },
+                { ".xhtml", "application/xhtml+xml" },
+                { ".xla", "application/vnd.ms-excel" },
+                { ".xlam", "application/vnd.ms-excel.addin.macroEnabled.12" },
+                { ".xlc", "application/vnd.ms-excel" },
+                { ".xld", "application/vnd.ms-excel" },
+                { ".xlk", "application/vnd.ms-excel" },
+                { ".xll", "application/vnd.ms-excel" },
+                { ".xlm", "application/vnd.ms-excel" },
+                { ".xls", "application/vnd.ms-excel" },
+                { ".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12" },
+                { ".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12" },
+                { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
+                { ".xlt", "application/vnd.ms-excel" },
+                { ".xltm", "application/vnd.ms-excel.template.macroEnabled.12" },
+                { ".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template" },
+                { ".xlw", "application/vnd.ms-excel" },
+                { ".xml", "text/xml" },
+                { ".xmta", "application/xml" },
+                { ".xof", "x-world/x-vrml" },
+                { ".XOML", "text/plain" },
+                { ".xpm", "image/x-xpixmap" },
+                { ".xps", "application/vnd.ms-xpsdocument" },
+                { ".xrm-ms", "text/xml" },
+                { ".xsc", "application/xml" },
+                { ".xsd", "text/xml" },
+                { ".xsf", "text/xml" },
+                { ".xsl", "text/xml" },
+                { ".xslt", "text/xml" },
+                { ".xsn", "application/octet-stream" },
+                { ".xss", "application/xml" },
+                { ".xtp", "application/octet-stream" },
+                { ".xwd", "image/x-xwindowdump" },
+                { ".z", "application/x-compress" },
+                { ".zip", "application/x-zip-compressed" },
+
+                #endregion
+
+            };
+
+        public static string GetMimeType(string extension)
+        {
+            if (extension == null)
+                throw new ArgumentNullException(nameof(extension));
+
+            if (!extension.StartsWith("."))
+                extension = "." + extension;
+
+            return Mappings.TryGetValue(extension, out var mime) ? mime : "application/octet-stream";
+        }
+
+    }
+}

+ 26 - 5
Common/qdr.fnd.core/Object/Extensions/ObjectExt.cs

@@ -1,23 +1,44 @@
-using System.Collections.Generic;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Quadarax.Foundation.Core.Reflection.Extensions;
 
 namespace Quadarax.Foundation.Core.Object.Extensions
 {
     public static class ObjectExt
     {
-        public static IDictionary<string, object> GetAllPropertyValues(this object obj)
+        public static IDictionary<string, object> GetAllPropertyValues(this object obj,string indentPrefix = null)
         {
             var props = new Dictionary<string, object>();
             if (obj == null)
                 return props;
- 
+
+            if (string.IsNullOrEmpty(indentPrefix))
+                indentPrefix = string.Empty;
+
             var type = obj.GetType();
-            foreach (var prop in type.GetProperties())
+            var propList = type.GetAllProperties();
+            foreach (var prop in propList)
             {
                 var val = prop.GetValue(obj, new object[] { });
-                props.Add(prop.Name, val);
+                if (val is IEnumerable enumerable)
+                {
+                    var cnt = 0;
+                    foreach (var item in enumerable)
+                    {
+                        var itemProps = item.GetAllPropertyValues(prop.Name + $"[{cnt}].");
+                        foreach (var ip in itemProps)
+                            props.Add(ip.Key, ip.Value);
+                        cnt++;
+                    }
+                }
+                else
+                    props.Add(indentPrefix + prop.Name, val);
             }
  
             return props;
         }
+
     }
 }

+ 20 - 0
Common/qdr.fnd.core/Reflection/Extensions/TypeExt.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
+using System.Security.Cryptography.X509Certificates;
 
 namespace Quadarax.Foundation.Core.Reflection.Extensions
 {
@@ -13,5 +14,24 @@ namespace Quadarax.Foundation.Core.Reflection.Extensions
             return methods.Where(x => x.IsDefined(typeof(TAttribute), bInherit)).ToArray();
         }
 
+
+        public static IList<PropertyInfo> GetAllProperties(this Type type)
+        {
+            if (type == null)
+                return new List<PropertyInfo>();
+
+            var propList = new List<PropertyInfo>(type.GetProperties(BindingFlags.Instance | BindingFlags.Public)); //type.GetTypeInfo().DeclaredProperties;
+            if (type.GetTypeInfo().BaseType != null)
+            {
+                var nestedProps = GetAllProperties(type.GetTypeInfo().BaseType);
+                foreach (var prop in nestedProps)
+                {
+                    if (propList.Any(x=>x.Name == prop.Name)) continue;
+                    propList.Add(prop);
+                }
+            }
+            return propList;
+        }
+
     }
 }

+ 1 - 1
Console/Commands/Base/AbstractListScopedCommand.cs

@@ -1,9 +1,9 @@
 using System;
 using System.Collections.Generic;
-using System.Text;
 using Quadarax.Foundation.Core.QConsole;
 using Quadarax.Foundation.Core.QConsole.Argument;
 using Quadarax.Foundation.Core.QConsole.Value;
+// ReSharper disable InconsistentNaming
 
 namespace BO.Console.Commands.Base
 {

+ 2 - 3
Console/Commands/Base/AbstractPostCommand.cs

@@ -111,12 +111,11 @@ namespace BO.Console.Commands.Base
         {
             base.OnValidateArguments();
 
-            var fileSystem = new FileSystem();
-            if (!fileSystem.File.Exists(GetArgumentValueOrDefault<string>(CS_ARG_NAME_IN)))
+            if (!FileSystem.File.Exists(GetArgumentValueOrDefault<string>(CS_ARG_NAME_IN)))
                 throw new FileNotFoundException($"Cannot find input file '{GetArgumentValueOrDefault<string>(CS_ARG_NAME_IN)}'");
 
 
-            var binder = new Binder(fileSystem);
+            var binder = new Binder(FileSystem);
             Data = binder.Load<List<TData>>(GetArgumentValueOrDefault<string>(CS_ARG_NAME_IN));
 
         }

+ 71 - 0
Console/Commands/Base/AbstractWorkspaceCommand.cs

@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Quadarax.Foundation.Core.QConsole;
+using Quadarax.Foundation.Core.QConsole.Argument;
+using Quadarax.Foundation.Core.QConsole.Value;
+
+namespace BO.Console.Commands.Base
+{
+    internal abstract class AbstractWorkspaceCommand : BaseCommand
+    {
+        #region *** Constants ***
+
+        private const string CS_ARG_NAME_APIK = "akey";
+        private const string CS_ARG_HINT_APIK = "<api_key>";
+        private const string CS_ARG_DESC_APIK = "Workspace related API-KEY, if not defined API-KEY is used from config.";
+
+        private const string CS_ARG_NAME_APIKP = "akeyp";
+        private const string CS_ARG_HINT_APIKP = "<api_key_password>";
+        private const string CS_ARG_DESC_APIKP = "Workspace related API-KEY password, if not defined API-KEY password is used from config.";
+
+        #endregion
+
+
+        #region *** Properties ***
+        protected string ApiKey { get; private set; }
+        protected string ApiKeyPassword { get; private set; }
+        #endregion
+
+        #region *** Constructors ***
+        protected AbstractWorkspaceCommand(Engine engine) : base(engine)
+        {
+        }
+        #endregion
+
+        #region *** Protected Overrides ***
+
+        protected override void BeginExecute()
+        {
+            base.BeginExecute();
+            ApiKey = Configuration.Defaults.Workspace.ApiKey;
+            ApiKeyPassword = Configuration.Defaults.Workspace.ApiKeyPassword;
+
+            var apiKey = GetArgumentValueOrDefault<string>(CS_ARG_NAME_APIK);
+            var apiKeyPassword = GetArgumentValueOrDefault<string>(CS_ARG_NAME_APIKP);
+
+            if (!string.IsNullOrEmpty(apiKey))
+                ApiKey = apiKey;
+            if (!string.IsNullOrEmpty(apiKeyPassword))
+                ApiKey = apiKeyPassword;
+
+            if (string.IsNullOrEmpty(ApiKey) || string.IsNullOrEmpty(ApiKeyPassword))
+                throw new ArgumentException(
+                    $"ApiKey or ApiKeyPassword must be specified by argument {Engine.Configuration.CharacterNamedArgumentSeparator}{CS_ARG_NAME_APIK} or {Engine.Configuration.CharacterNamedArgumentSeparator}{CS_ARG_NAME_APIKP} or specified in configuration file {Constants.CS_CONFIGURATION_FILE} in location 'defaults/workspace/api-key' or 'defaults/workspace/api-key-password'.");
+
+            WriteInfo($"Using API-KEY: {ApiKey}");
+            WriteDebugInfo($"Using API-KEY-PASSWORD: {ApiKeyPassword}");
+
+        }
+
+        protected override IEnumerable<AbstractArgument> OnSetupArguments()
+        {
+            var args =  base.OnSetupArguments().ToList();
+            args.Add(new NamedArgument(CS_ARG_NAME_APIK, CS_ARG_DESC_APIK,CS_ARG_HINT_APIK, TypeValuesEnum.String, string.Empty, false));
+            args.Add(new NamedArgument(CS_ARG_NAME_APIKP, CS_ARG_DESC_APIKP,CS_ARG_HINT_APIKP, TypeValuesEnum.String, string.Empty, false));
+            return args;
+        }
+
+        #endregion
+    }
+}

+ 58 - 7
Console/Commands/Base/BaseCommand.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.IO.Abstractions;
 using System.Linq;
 using System.Text.Json;
 using BO.Connector.Console;
@@ -12,6 +13,8 @@ using Quadarax.Foundation.Core.Object.Extensions;
 using Quadarax.Foundation.Core.QConsole;
 using Quadarax.Foundation.Core.QConsole.Argument;
 using Quadarax.Foundation.Core.QConsole.Command.Base;
+using Quadarax.Foundation.Core.QConsole.Value;
+// ReSharper disable InconsistentNaming
 
 namespace BO.Console.Commands.Base
 {
@@ -23,31 +26,74 @@ namespace BO.Console.Commands.Base
         private const string CS_ARG_HINT_DUMP = "<is_dump_reponses>";
         private const string CS_ARG_DESC_DUMP = "Flag if set dumps incomming json messages.";
 
+        private const string CS_ARG_NAME_DEBUG = "dbg";
+        private const string CS_ARG_HINT_DEBUG = "<is_debug_mode>";
+        private const string CS_ARG_DESC_DEBUG = "Flag if set writes debug messages.";
+
+        private const string CS_ARG_NAME_USER = "user";
+        private const string CS_ARG_HINT_USER = "<user_name>";
+        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_FORCE = "force";
+        private const string CS_ARG_HINT_FORCE = "<is_force>";
+        private const string CS_ARG_DESC_FORCE = "Flag if set, then overrite existing entity, otherwise cancel operation.";
+
+
         #endregion
 
 
         protected Connection Client { get; private set; }
         protected Configuration Configuration { get; private set; }
 
+        protected string UserName { get; private set; }
+        protected bool? IsForce { get; private set; }
+
+        protected IFileSystem FileSystem { get; }
+
         protected BaseCommand(Engine engine) : base(engine)
         {
+            FileSystem = new FileSystem();
         }
 
         protected override void OnInitialize()
         {
             base.OnInitialize();
-            Configuration = JsonSerializer.Deserialize<Configuration>(File.ReadAllText("console.json"));
+            Configuration = JsonSerializer.Deserialize<Configuration>(File.ReadAllText(Constants.CS_CONFIGURATION_FILE));
             Client = new Connection(Configuration.ApiBaseUrl, Configuration.ApiTimeout, this, GetArgumentValueOrDefault<bool>(CS_ARG_NAME_DUMP));
             Client.Open(Configuration.ApiUser, Configuration.ApiUserPwd, Configuration.ApiMagic);
         }
 
+        protected override void OnValidateArguments()
+        {
+            base.OnValidateArguments();
+            if (ContainsArgument(CS_ARG_NAME_USER))
+                UserName = GetArgumentValueOrDefault<string>(CS_ARG_NAME_USER);
+
+            if (ContainsArgument(CS_ARG_NAME_FORCE))
+                IsForce = WasArgumentSpecified(CS_ARG_NAME_FORCE);
+
+            if (WasArgumentSpecified(CS_ARG_NAME_DEBUG))
+                DebugConsoleWriter.IsWriteDebugEnabled = true;
+        }
+
         protected override IEnumerable<AbstractArgument> OnSetupArguments()
         {
             var args = base.OnSetupArguments().ToList();
             args.Add(new FlagArgument(CS_ARG_NAME_DUMP, CS_ARG_DESC_DUMP, CS_ARG_HINT_DUMP, false));
+            args.Add(new FlagArgument(CS_ARG_NAME_DEBUG, CS_ARG_DESC_DEBUG, CS_ARG_HINT_DEBUG, false));
             return args;
         }
 
+        protected void AppendUserArgument(IList<AbstractArgument> args, string alternativeUserDescription = null)
+        {
+            args.Add(new NamedArgument(CS_ARG_NAME_USER, string.IsNullOrEmpty(alternativeUserDescription)? CS_ARG_DESC_USER : alternativeUserDescription, CS_ARG_HINT_USER, TypeValuesEnum.String, string.Empty, false));
+        }
+
+        protected void AppendForceArgument(IList<AbstractArgument> args, string alternativeUserDescription = null)
+        {
+            args.Add(new FlagArgument(CS_ARG_NAME_FORCE, string.IsNullOrEmpty(alternativeUserDescription) ? CS_ARG_DESC_FORCE : alternativeUserDescription,CS_ARG_HINT_FORCE, false));
+        }
+
         public void Log(LogSeverityEnum type, string message, Exception e = null)
         {
             WriteInfo($"{type}: {message}" + (e == null ? "" : e.Message));
@@ -56,14 +102,19 @@ namespace BO.Console.Commands.Base
         protected void WriteDtoToConsole(IDto dto)
         {
             var props = dto.GetAllPropertyValues();
-            using(var wName = new ConsoleWriter(ConsoleColor.White))
-            using(var wValue = new ConsoleWriter(ConsoleColor.Yellow))
-                foreach (var prop in props)
+            using (var wName = new ConsoleWriter(ConsoleColor.White))
+            {
+                using (var wValue = new ConsoleWriter(ConsoleColor.Yellow))
                 {
-                    wName.Write(prop.Key);
-                    wName.Write("\t:\t");
-                    wValue.WriteLine(prop.Value?.ToString());
+                    var maxKeyLength = props.Keys.Max(x=>x.Length);
+                    foreach (var prop in props)
+                    {
+                        wName.Write(prop.Key);
+                        wName.Write(new string(' ',maxKeyLength - prop.Key.Length) + " :\t");
+                        wValue.WriteLine(prop.Value?.ToString());
+                    }
                 }
+            }
         }
     }
 }

+ 11 - 0
Console/Commands/Base/SinglePaging.cs

@@ -0,0 +1,11 @@
+using Quadarax.Foundation.Core.Data.Interface;
+
+namespace BO.Console.Commands.Base
+{
+    internal class SinglePaging : PagingDto
+    {
+        public SinglePaging() : base(1, 1)
+        {
+        }
+    }
+}

+ 2 - 2
Console/Commands/Catalogues/MimeTypesGetCommand.cs

@@ -7,7 +7,7 @@ using Quadarax.Foundation.Core.Value;
 namespace BO.Console.Commands.Catalogues
 {
     [CommandDefinition]
-    internal class MimTypesGetCommand : AbstractListScopedCommand<AppServer.Metadata.Enums.MimeTypeScopeEnums>
+    internal class MimeTypesGetCommand : AbstractListScopedCommand<AppServer.Metadata.Enums.MimeTypeScopeEnums>
     {
         #region *** Constants ***
 
@@ -25,7 +25,7 @@ namespace BO.Console.Commands.Catalogues
 
         #region *** Constructor ***
 
-        public MimTypesGetCommand(Engine engine) : base(engine)
+        public MimeTypesGetCommand(Engine engine) : base(engine)
         {
         }
 

+ 118 - 13
Console/Commands/Document/DocumentAddCommand.cs

@@ -1,44 +1,149 @@
-using BO.AppServer.Metadata.Dto;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BO.AppServer.Metadata.Dto;
 using BO.Console.Commands.Base;
+using Quadarax.Foundation.Core.IO.MimeTypes;
 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;
+using Quadarax.Foundation.Core.Value.Extensions;
 
-namespace BO.Console.Commands.Workspace
+// ReSharper disable InconsistentNaming
+
+namespace BO.Console.Commands.Document
 {
     [CommandDefinition]
-    internal class WorkspaceAddCommand : AbstractPostCommand<WorkspaceCDto, WorkspaceRDto>
+    internal class DocumentAddCommand : AbstractWorkspaceCommand 
     {
         #region *** Constants ***
-        private const string CS_CMD_WKSPADD_NAME = "workspace_add";
-        private const string CS_CMD_WKSPADD_DESCR = "Add new workspace/s assigned to user by import file.";
+        private const string CS_CMD_DOCADD_NAME = "document_add";
+        private const string CS_CMD_DOCADD_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_NAME_ARTS = "arts";
+        private const string CS_ARG_HINT_ARTS = "<artifact_files>";
+        private const string CS_ARG_DESC_ARTS = "Artifact file names (separated by ';') or single file name.";
+
+        private const string CS_ARG_DESC_FORCE = "Flag if set, then appends artifact to existing metadocument, otherwise cancel operation.";
+
+        private const string CS_ARG_NAME_OUTPUT = "setout";
+        private const string CS_ARG_HINT_OUTPUT = "<is_art_output>";
+        private const string CS_ARG_DESC_OUTPUT = "Flag if set, then appends artifact as output (output from PS) otherwise set as input (input from user).";
 
         #endregion
 
         #region *** Properties ***
 
-        public override string Name => CS_CMD_WKSPADD_NAME;
-        public override string Description => CS_CMD_WKSPADD_DESCR;
+        public override string Name => CS_CMD_DOCADD_NAME;
+        public override string Description => CS_CMD_DOCADD_DESCR;
+
+
+        protected string DocumentName { get; set; }
+        protected IList<string> ArtifactFiles { get; set; }
 
         #endregion
 
         #region *** Constructor ***
-        public WorkspaceAddCommand(Engine engine) : base(engine)
+        public DocumentAddCommand(Engine engine) : base(engine)
         {
         }
         #endregion
 
         #region *** EXECUTE ***
-        protected override  WorkspaceRDto IterationExecute(WorkspaceCDto data)
+        protected override Result OnExecute()
+        {
+            // 1. Check if document exists
+            WriteDebugInfo($"Checking existence of document '{DocumentName}' in workspace...");
+            var document = Client.GetWorkspaceDocumentByNameAsync(ApiKey, ApiKeyPassword, DocumentName, UserName).Result;
+            if (document!=null)
+            {
+                WriteDebugInfo($"Document '{DocumentName}' exists [{document.Id}] in workspace...");
+            }
+            else
+            {
+                // 2. Create new document
+                WriteDebugInfo($"Document '{DocumentName}' not exists in workspace - creating...");
+                document = Client.CreateWorkspaceDocumentAsync(ApiKey, ApiKeyPassword, new DocumentCDto(DocumentName), UserName).Result;
+                WriteInfo($"Document '{document.Name}' [{document.Id}] created.");
+            }
+            // 3. Append artifacts
+            WriteInfo($"Appending {ArtifactFiles.Count} artifacts...");
+            //var appendArtTasks = new List<Task>();
+            foreach (var artFile in ArtifactFiles)
+            {
+                //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))
+                    {
+                        art = Client.AppendWorkspaceDocumentArtifactAsync(ApiKey, ApiKeyPassword, document.Id,
+                            FileSystem.Path.GetFileName(artFile), MimeTypeUtil.GetMimeType(FileSystem.Path.GetExtension(artFile)), 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());
+            return new Result();
+        }
+        #endregion
+
+        #region *** Protected overrides ***
+        protected override void OnValidateArguments()
         {
-            WriteDebugInfo($"Workspace '{data.Name}' for user '{data.AssignedUserName}' starts creating...");
-            var result = Client.CreateWorkspaceAsync(data).Result;
-            WriteCaption($"Workspace '{data.Name}' for user '{data.AssignedUserName}' created.");
-            return result;
+            base.OnValidateArguments();
+            DocumentName = GetArgumentValueOrDefault<string>(CS_ARG_NAME_NAME);
+
+            var artifactFiles = GetArgumentValueOrDefault<string>(CS_ARG_NAME_ARTS);
+            ArtifactFiles = artifactFiles.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
+            var sb = new StringBuilder();
+            foreach (var artFile  in ArtifactFiles)
+            {
+                if (!FileSystem.File.Exists(artFile))
+                    sb.Append("'").Append(artFile).Append("',");
+            }
+
+            if (sb.Length > 0)
+            {
+                sb.Remove(sb.Length - 1, 1);
+                throw new FileNotFoundException($"Artifact files: {sb} not found!");
+            }
+
+            if (string.IsNullOrEmpty(DocumentName))
+                DocumentName = FileSystem.Path.GetFileNameWithoutExtension(ArtifactFiles.First());
         }
 
         #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));
+            args.Add(new NamedArgument(CS_ARG_NAME_ARTS, CS_ARG_DESC_ARTS,CS_ARG_HINT_ARTS, TypeValuesEnum.String, string.Empty, true));
+            AppendUserArgument(args, CS_ARG_DESC_USER);
+
+            args.Add(new FlagArgument(CS_ARG_NAME_OUTPUT, CS_ARG_DESC_OUTPUT,CS_ARG_HINT_OUTPUT, false));
+            AppendForceArgument(args, CS_ARG_DESC_FORCE);
+            return args.ToArray();
+        }
+
         #endregion
     }
 }

+ 4 - 5
Console/Commands/Document/DocumentsGetCommand.cs

@@ -2,21 +2,20 @@
 using System.Linq;
 using BO.AppServer.Metadata.Enums;
 using BO.Console.Commands.Base;
-using Microsoft.VisualBasic;
 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.Workspace
+namespace BO.Console.Commands.Document
 {
     [CommandDefinition]
-    internal class WorkspacesGetCommand : AbstractListScopedCommand<AppServer.Metadata.Enums.WorkspaceScopeEnums>
+    internal class DocumentsGetCommand : AbstractListScopedCommand<WorkspaceScopeEnums>
     {
         #region *** Constants ***
 
-        private const string CS_CMD_WRKSPGET_NAME = "workspace_list";
+        private const string CS_CMD_WRKSPGET_NAME = "document_list";
         private const string CS_CMD_WRKSPGET_DESCR = "Get list of workspaces by specification scope.";
 
 
@@ -35,7 +34,7 @@ namespace BO.Console.Commands.Workspace
 
         #region *** Constructor ***
 
-        public WorkspacesGetCommand(Engine engine) : base(engine)
+        public DocumentsGetCommand(Engine engine) : base(engine)
         {
         }
 

+ 41 - 0
Console/Commands/Workspace/WorkspaceGetCommand.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using BO.Console.Commands.Base;
+using Quadarax.Foundation.Core.QConsole;
+using Quadarax.Foundation.Core.QConsole.Attributes;
+using Quadarax.Foundation.Core.Value;
+
+namespace BO.Console.Commands.Workspace
+{
+    [CommandDefinition]
+    internal class WorkspaceGetCommand : AbstractGetCommand
+    {
+        #region *** Constants ***
+        private const string CS_CMD_WRKSPGET_NAME = "workspace_get";
+        private const string CS_CMD_WRKSPGET_DESCR = "Get single workspace by specification.";
+        #endregion
+
+        #region *** Properties ***
+
+        public override string Name => CS_CMD_WRKSPGET_NAME;
+        public override string Description => CS_CMD_WRKSPGET_DESCR;
+
+        #endregion
+
+        #region *** Constructor ***
+        public WorkspaceGetCommand(Engine engine) : base(engine)
+        {
+        }
+        #endregion
+
+        #region *** EXECUTE ***
+        protected override Result OnExecute()
+        {
+            var workspace = Client.GetWorkspaceAsync(IdentificationType, IdentificationValue).Result;
+            WriteDtoToConsole(workspace);
+            return new Result();
+        }
+        #endregion
+    }
+}

+ 1 - 2
Console/Commands/Workspace/WorkspacesGetCommand.cs

@@ -2,7 +2,6 @@
 using System.Linq;
 using BO.AppServer.Metadata.Enums;
 using BO.Console.Commands.Base;
-using Microsoft.VisualBasic;
 using Quadarax.Foundation.Core.QConsole;
 using Quadarax.Foundation.Core.QConsole.Argument;
 using Quadarax.Foundation.Core.QConsole.Attributes;
@@ -12,7 +11,7 @@ using Quadarax.Foundation.Core.Value;
 namespace BO.Console.Commands.Workspace
 {
     [CommandDefinition]
-    internal class WorkspacesGetCommand : AbstractListScopedCommand<AppServer.Metadata.Enums.WorkspaceScopeEnums>
+    internal class WorkspacesGetCommand : AbstractListScopedCommand<WorkspaceScopeEnums>
     {
         #region *** Constants ***
 

+ 1 - 0
Console/Console.csproj

@@ -5,6 +5,7 @@
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <AssemblyName>BO.Console</AssemblyName>
     <RootNamespace>BO.Console</RootNamespace>
+    <LangVersion>default</LangVersion>
   </PropertyGroup>
 
   <ItemGroup>

+ 8 - 0
Console/Constants.cs

@@ -0,0 +1,8 @@
+namespace BO.Console
+{
+    // ReSharper disable  InconsistentNaming
+    internal class Constants
+    {
+        public const string CS_CONFIGURATION_FILE = "console.json";
+    }
+}

+ 19 - 0
Console/Options/Configuration.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Text.Json.Serialization;
+using BO.AppServer.Metadata.Enums;
 
 namespace BO.Console.Options
 {
@@ -19,5 +20,23 @@ namespace BO.Console.Options
 
         [JsonPropertyName("default-page-size")]
         public int DefaultPageSize { get; set; }
+
+        [JsonPropertyName("defaults")]
+        public DefaultsCfg Defaults { get; set; }
+
+        public class DefaultsCfg
+        {
+            [JsonPropertyName("workspace")]
+            public DefaultWorkspaceCfg Workspace { get; set; }
+        }
+
+
+        public class DefaultWorkspaceCfg
+        {
+            [JsonPropertyName("api-key")]
+            public string ApiKey { get; set; }
+            [JsonPropertyName("api-key-password")]
+            public string ApiKeyPassword { get; set; }
+        }
     }
 }

+ 8 - 0
Console/Program.cs

@@ -3,6 +3,7 @@ using System.Reflection;
 using BO.AppServer.Metadata.Dto;
 using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;
 using Quadarax.Foundation.Core.Json;
+using Quadarax.Foundation.Core.Object.Extensions;
 using Quadarax.Foundation.Core.QConsole;
 using Quadarax.Foundation.Core.QConsole.Configuration;
 using Quadarax.Foundation.Core.QConsole.Context;
@@ -16,6 +17,13 @@ namespace BO.Console
         static void Main(string[] args)
         {
             /*
+            var str = new StructureRDto();
+            var x = str.GetAllPropertyValues();
+            int a = 0;
+            */
+            /*
+            document_add -arts:doc1_file1.bin;doc1_file2.bin -force -user:test -dbg -dump
+
             var user = new UserCDto()
             {
                 Name = "foo",

+ 1 - 1
Console/Properties/launchSettings.json

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

二進制
Console/Samples/doc1_file1.bin


二進制
Console/Samples/doc1_file2.bin


+ 3 - 0
Console/Samples/upload_document_2_art.cmd

@@ -0,0 +1,3 @@
+REM ** Uploads 2 artifacts to one new created document (if not exists - otherwise append arts) in default workspace (api-key is defined in console.json) **
+REM ** AppServer must be started first / this script must be run in same directory as BO.Console**
+Bo.Console document_add -arts:doc1_file1.bin;doc1_file2.bin -force -user:test

+ 7 - 1
Console/console.json

@@ -4,5 +4,11 @@
   "api-pwd": "A1Abastr0vA",
   "api-magic": "6DEC4167-57A6-4333-92DA-ADE009A97DE9",
   "api-timeout": "00:10:00",
-  "default-page-size": 50
+  "default-page-size": 50,
+  "defaults" : {
+    "workspace": {
+      "api-key": "33511A7C-9F05-4058-A840-85A589973BC2",
+      "api-key-password": "test"
+    } 
+  } 
 }