diff --git a/Documents/Boards/xDashboard.kanban b/Documents/Boards/xDashboard.kanban index 61abadc..da17f1d 100644 --- a/Documents/Boards/xDashboard.kanban +++ b/Documents/Boards/xDashboard.kanban @@ -570,6 +570,11 @@ "id": "07602ca6-38b8-4d19-b0f6-8730e11de1df", "title": "Add Support for Files Attachment in AI Asking ", "checked": false + }, + { + "id": "149c4343-16ab-4ac8-899b-23d202000b85", + "title": "Add Support For File Content Extracting based On Level 1 and Level 2", + "checked": false } ], "comments": [] diff --git a/Documents/GeneratedContext/xDashboard-xFileService-20260930_212429.txt b/Documents/GeneratedContext/xDashboard-xFileService-20260930_212429.txt new file mode 100644 index 0000000..78cde83 --- /dev/null +++ b/Documents/GeneratedContext/xDashboard-xFileService-20260930_212429.txt @@ -0,0 +1,3269 @@ + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Configurations\Entities\XFileEntityConfiguration.cs + +using System; +using xDataService.Providers; +using xFileService.Constants; +using xFileService.Models.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace xFileService.Configurations.Entities +{ + public class XFileEntityConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable(XFileServiceConstants.XFileTable); + } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Constants\XFileServiceConstants.cs + +using xFileService.Models.Entities; + +namespace xFileService.Constants +{ + public struct XFileServiceConstants + { + // + // Service Extensions Log Tag ... + public const string XFileServiceDILogTag = "XFileService"; + + // + // Table Mapping Identifiers ... + public const string XFileTable = "Files"; + + // + // GraphQL Collection Mappings ... + public const string XFileSingleName = "file"; + public const string XFileCollectionName = "files"; + + // + // Hubs ... + public const string XFileDtoHub = "fileDto"; + public const string XFileEntityHub = "fileEntity"; + + // + // Custome Constants ... + public const string XFileTagProvided = nameof(XFile); + + public const string XAdminRole = "admin"; + + // + // EF Repositories Helper Extensions ... + public const string GetXFileEFRepositoryDescriptor = "GetXFileEFRepositoryDescriptor"; + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Controllers\XFileProviderControllerBase.cs + +using System; +using System.Linq; +using System.Threading; +using xCommons.Providers; +using xCommons.Extensions; +using xDataService.Models; +using xExceptions.Constants; +using System.Threading.Tasks; +using xCommons.Configurations; +using xFileService.Interfaces; +using xTagService.Controllers; +using Microsoft.AspNetCore.Mvc; +using xFileService.Models.Dtos; +using Microsoft.AspNetCore.Http; +using System.Collections.Generic; +using xFileService.Providers.Dtos; +using xIdentityService.Interfaces; +using Microsoft.Extensions.Logging; +using xFileService.Models.Entities; +using Microsoft.EntityFrameworkCore.Query; + +namespace xFileService.Controllers +{ + public abstract class XFileProviderControllerBase : XBaseProvidedHasTagControllerBase, IXFileProviderController + { + private readonly IXFileProvider provider; + + protected XFileProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXFileProvider provider, + IXFileTagProvided tagProvided, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider, + provider, + tagProvided, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { + this.provider = provider; + } + + // + #region Tools ... + /// + /// Stream Specified File ... + /// + /// + /// + /// + [HttpGet("{id}/Stream/ById")] + public virtual async Task Stream( + [FromRoute] Guid id, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider.Stream( + id: id, + cancellationToken: cancellationToken + ); + + // + return result; + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Stream Specified File ... + /// + /// + /// + /// + [HttpGet("{name}/Stream/ByName")] + public virtual async Task Stream( + [FromRoute] string name, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider.Stream( + name: name, + cancellationToken: cancellationToken + ); + + // + return result; + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Download Specified File ... + /// + /// + /// + /// + [HttpGet("{id}/Download/ById")] + public virtual async Task Download( + [FromRoute] Guid id, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider.Download( + id: id, + cancellationToken: cancellationToken + ); + + // + return result; + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Download Specified File ... + /// + /// + /// + /// + [HttpGet("{name}/Download/ByName")] + public virtual async Task Download( + string name, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider.Download( + name: name, + cancellationToken: cancellationToken + ); + + // + return result; + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Upload Files ... + /// + /// + /// + /// + [HttpPost("Upload")] + [RequestSizeLimit(966_367_641)] + public virtual async Task>> Upload( + [FromForm] IFormFileCollection files, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Retrieve User Info ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Upload( + files: files, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Specified Files ... + /// + /// + /// + /// + [HttpDelete("")] + public virtual async Task>> Remove( + [FromQuery] string ids, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Retrieve User Info ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Remove( + ids: ids, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Reference ... + /// + /// Check an Item Has Reference to Specified ProvideFor and Specified forProvidedId ... + /// + /// + /// + /// + /// + /// + [HttpGet("{id}/Reference/IsProvidedFor")] + public virtual async Task> IsProvidedFor( + [FromRoute] Guid id, + [FromQuery] string providedFor, + [FromQuery] object forProvidedId, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotEmpty(providedFor) + .AddNotNull(forProvidedId) + .ValidateGroupAsync(); + + // + var result = await provider.IsProvidedFor( + id: id, + providedFor: providedFor, + forProvidedId: forProvidedId, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add a Reference to Item for Specified ProvideFor and Specified forProvidedId ... + /// + /// + /// + /// + /// + /// + /// + [HttpPost("{id}/Reference")] + public virtual async Task AddReference( + [FromRoute] Guid id, + [FromQuery] string providedFor, + [FromQuery] object forProvidedId, + [FromQuery] int forIndex = 0, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotEmpty(providedFor) + .AddNotNull(forProvidedId) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + await provider.AddReference( + id: id, + forIndex: forIndex, + userInfo: userInfo, + providedFor: providedFor, + connectionId: connectionId, + forProvidedId: forProvidedId, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove a Reference from Item for Specified ProvideFor and Specified forProvidedId ... + /// + /// + /// + /// + /// + /// + /// + [HttpDelete("{id}/Reference")] + public virtual async Task RemoveReference( + [FromRoute] Guid id, + [FromQuery] string providedFor, + [FromQuery] object forProvidedId, + [FromQuery] int forIndex = 0, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotEmpty(providedFor) + .AddNotNull(forProvidedId) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + await provider.RemoveReference( + id: id, + forIndex: forIndex, + userInfo: userInfo, + providedFor: providedFor, + connectionId: connectionId, + forProvidedId: forProvidedId, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve References of Specified Itemfor providedFor ... + /// + /// + /// + /// + /// + /// + [HttpGet("{id}/References")] + public virtual async Task>>> GetReferences( + [FromRoute] Guid id, + [FromQuery] string providedFor, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotEmpty(providedFor) + .ValidateGroupAsync(); + + // + var result = await provider.GetReferences( + id: id, + providedFor: providedFor, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Controllers\XFileRepositoryControllerBase.cs + +using System; +using System.Linq; +using xCommons.Providers; +using xCommons.Configurations; +using xDataService.Interfaces; +using xDataService.Controllers; +using Microsoft.Extensions.Logging; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; +using Microsoft.EntityFrameworkCore.Query; + +namespace xFileService.Controllers +{ + public abstract class XFileRepositoryControllerBase : XBaseRepositoryController, IXBaseRepositoryController + { + protected XFileRepositoryControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXFileRepository repository, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repository, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Controllers\XFileRepositoryProviderControllerBase.cs + +using System; +using System.Linq; +using xCommons.Providers; +using xCommons.Configurations; +using xPushService.Interfaces; +using xPushService.Controllers; +using xIdentityService.Interfaces; +using Microsoft.Extensions.Logging; +using xFileService.Models.Entities; +using xFileService.Providers.Entities; +using xFileService.Interfaces.Entities; +using Microsoft.EntityFrameworkCore.Query; + +namespace xFileService.Controllers +{ + public abstract class XFileRepositoryProviderControllerBase : XBaseRepositoryProviderController, IXBaseRepositoryProviderController + { + protected XFileRepositoryProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXFileRepositoryProvider provider, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider, + provider, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Controllers\XFileServiceControllerBase.cs + +using System; +using System.Linq; +using xCommons.Providers; +using xCommons.Configurations; +using xDataService.Interfaces; +using xDataService.Controllers; +using xFileService.Models.Dtos; +using Microsoft.Extensions.Logging; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Entities; +using Microsoft.EntityFrameworkCore.Query; + +namespace xFileService.Controllers +{ + public abstract class XFileServiceControllerBase : XBaseServiceController, IXBaseServiceController + { + protected XFileServiceControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXFileRepositoryService repositoryService, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repositoryService, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Controllers\XFileServiceProviderControllerBase.cs + +using System; +using System.Linq; +using xCommons.Providers; +using xCommons.Configurations; +using xPushService.Interfaces; +using xFileService.Models.Dtos; +using xPushService.Controllers; +using xFileService.Providers.Dtos; +using xIdentityService.Interfaces; +using Microsoft.Extensions.Logging; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Entities; +using Microsoft.EntityFrameworkCore.Query; + +namespace xFileService.Controllers +{ + public abstract class XFileServiceProviderControllerBase : XBaseServiceProviderController, IXBaseServiceProviderController + { + protected XFileServiceProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXFileServiceProvider provider, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider, + provider, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\DI\XDIHelperExtension.cs + +using System; +using xDataService.Db; +using xDataService.DI; +using xPushService.DI; +using xCommons.Helpers; +using xCommons.Extensions; +using xDataService.Models; +using xPushService.Helpers; +using xFileService.Helpers; +using xExceptions.Constants; +using xDataService.Constants; +using xFileService.Providers; +using xFileService.Constants; +using xFileService.Interfaces; +using xFileService.Providers.Dtos; +using Microsoft.AspNetCore.Builder; +using xFileService.Interfaces.Dtos; +using xFileService.Providers.Entities; +using xFileService.Interfaces.Entities; +using Microsoft.Extensions.DependencyInjection; + +namespace xFileService.DI +{ + public static class XDIHelperExtension + { + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXFileService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + AddFileService( + contextType: null, + services: services, + lifeTime: lifeTime, + repositoryType: repositoryType + ); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + public static void AddXFileService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + where TContext : XDbContext + { + AddFileService( + services: services, + lifeTime: lifeTime, + contextType: typeof(TContext), + repositoryType: repositoryType + ); + } + + /// + /// Use XFileService Middleware ... + /// + /// + /// + public static void UseXFileService( + this IApplicationBuilder app, + bool isDevelopmentEnvironment = false + ) + { + // + #region Using XFile Hubs ... + // + var pushHelper = new XPushServiceHelper(); + + // + pushHelper.AddHub(XFileServiceConstants.XFileDtoHub); + pushHelper.AddHub(XFileServiceConstants.XFileEntityHub); + + // + app.UseXPushService(pushHelper); + #endregion + + // + // Using XFile Repository Descriptor ... + if (!descriptor.IsNull()) + { + // + app.UseXRepository( + descriptor: descriptor, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + } + + // + #region Private ... + private static XRepositoryDescriptor descriptor = null; + + /// + /// a LogTag for Service ... + /// + private static string XLogTag = XFileServiceConstants.XFileServiceDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + private static void AddFileService( + IServiceCollection services, + XRepositoryType repositoryType, + Type contextType = null, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + var exception = XException.InvalidArgs.ToException(); ; + + // + // Services ... + var isServicesExists = !services.IsNull(); + if (!isServicesExists) + { + // + Log("Service Registration failed, services not provided ..."); + throw exception; + } + + // + // Lifetime ... + var isLifetimeExists = !lifeTime.IsNull(); + if (!isLifetimeExists) + { + // + Log("Service Registration failed, lifetime not provided ..."); + throw exception; + } + + // + // Validate Repository Type ... + var isRepositoryTypeValid = + (repositoryType == XRepositoryType.EF && + !contextType.IsNull()) || + ((repositoryType == XRepositoryType.Mongo || + repositoryType == XRepositoryType.InMemory) && + contextType.IsNull()); + if (!isRepositoryTypeValid) + { + // + Log("Service Registration failed, Invalid Repository Type and Context Type ..."); + throw exception; + } + + // + // Maske Instance of Repository Descriptor + // Based on Provided Type ... + switch (repositoryType) + { + // + case XRepositoryType.EF: + // + descriptor = typeof(XFileServiceHelper) + .InvokeGenericMethod( + args: null, + runtimeType: contextType, + methodName: XFileServiceConstants.GetXFileEFRepositoryDescriptor + ); + break; + + // + case XRepositoryType.Mongo: + descriptor = XFileServiceHelper.GetXFileMongoRepositoryDescriptor(); + break; + + // + case XRepositoryType.InMemory: + descriptor = XFileServiceHelper.GetXFileInMemoryRepositoryDescriptor(); + break; + } + + // + // Register XFile Repository Descriptor ... + services.AddXRepository( + lifeTime: lifeTime, + descriptor: descriptor + ); + + // + // Register Dto Services ... + services.Add( + new ServiceDescriptor(typeof(IXFileServiceProvider), typeof(XFileServiceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXFileRepositoryService), typeof(XFileRepositoryService), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXFileRepositoryProvider), typeof(XFileRepositoryProvider), lifeTime) + ); + + // + // Register Additional Service ... + services.Add( + new ServiceDescriptor(typeof(IXFileTagProvider), typeof(XFileTagProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXFileTagProvided), typeof(XFileTagProvided), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXFileProvider), typeof(XFileProvider), lifeTime) + ); + + // + Log("Service Regitration Succeed ..."); + } + #endregion + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Extensions\XModelExtensions.cs + +using System.Linq; +using xCommons.Extensions; +using xDataService.Models; +using xDataService.Extensions; +using xFileService.Models.Dtos; +using System.Collections.Generic; +using xFileService.Models.Entities; + +namespace xFileService.Extensions +{ + public static class XModelExtensions + { + /// + /// Extract References of a Model ... + /// + /// + /// + public static IList> GetReferences( + this XFile source + ) + { + // + var result = new List>(); + + // + if ( + !source.IsNullOrDefault() && + !source.References.IsNullOrEmpty() + ) + { + // + result = source.References + .ParseXReferenceList() + .ToList(); + } + + // + return result; + } + + /// + /// Extract References of a Model ... + /// + /// + /// + public static IList> GetReferences( + this XFileDto source + ) + { + // + var result = new List>(); + + // + if ( + !source.IsNullOrDefault() && + !source.References.IsNullOrEmpty() + ) + { + // + result = source.References + .ParseXReferenceList() + .ToList(); + } + + // + return result; + } + + /// + /// Update a Model's References by Providing References List ... + /// + /// + /// + /// + /// + public static XFile UpdateReferences( + this XFile source, + IList> references, + bool forceClean = true + ) + { + // + XFile result = source; + + // + if (!source.IsNullOrDefault()) + { + // + result = source; + + // + if (forceClean) + { + result.References = string.Empty; + } + + // + if (!references.IsNull() && + references.HasChild()) + { + result.References = references.ToXReferenceString(); + } + } + + // + return result; + } + + /// + /// Update a Model's References by Providing References List ... + /// + /// + /// + /// + /// + public static XFileDto UpdateReferences( + this XFileDto source, + IList> references, + bool forceClean = true + ) + { + // + XFileDto result = source; + + // + if (!source.IsNullOrDefault()) + { + // + result = source; + + // + if (forceClean) + { + result.References = string.Empty; + } + + // + if (!references.IsNull() && + references.HasChild()) + { + result.References = references.ToXReferenceString(); + } + } + + // + return result; + } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Helpers\XFileServiceHelper.cs + +using System; +using GraphQL.Types; +using xDataService.Db; +using xDataService.Models; +using xDataService.Providers; +using xFileService.Models.Entities; +using xFileService.Providers.GraphQL; +using xFileService.Providers.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Helpers +{ + public static class XFileServiceHelper + { + // + #region EF Repository Descriptor Provider(s) ... + public static XEFRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileEFRepository, + TContext + > GetXFileEFRepositoryDescriptor() + where TContext : XDbContext + { + // + var result = new XEFRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileEFRepository, + TContext + >(); + + // + return result; + } + + public static XEFRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileEFRepository, + TContext, + IXFileSeeder, + TSeederImplementation + > GetXFileEFRepositoryDescriptor() + where TContext : XDbContext + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XEFRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileEFRepository, + TContext, + IXFileSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region Mongo Repository Descriptor Provider(s) ... + public static XMongoRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileMongoRepository + > GetXFileMongoRepositoryDescriptor() + { + // + var result = new XMongoRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileMongoRepository + >(); + + // + return result; + } + + public static XMongoRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileMongoRepository, + IXFileSeeder, + TSeederImplementation + > GetXFileMongoRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XMongoRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileMongoRepository, + IXFileSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region InMemory Repository Descriptor Provider(s) ... + public static XInMemoryRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileInMemoryRepository + > GetXFileInMemoryRepositoryDescriptor() + { + // + var result = new XInMemoryRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileInMemoryRepository + >(); + + // + return result; + } + + public static XInMemoryRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileInMemoryRepository, + IXFileSeeder, + TSeederImplementation + > GetXFileInMemoryRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XInMemoryRepositoryDescriptor< + XFile, + Guid, + IXFileKeyGenerator, + XFileKeyGenerator, + IXFileRepositoryEvents, + XFileRepositoryEvents, + XFileGraphType, + GuidGraphType, + XFileGraphQuery, + IXFileGraphQLTypeHelper, + XFileGraphQLTypeHelper, + XFileGraphSchema, + IXFileRepository, + XFileInMemoryRepository, + IXFileSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\IXFileProvider.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using xIdentityModels.Models; +using xTagService.Interfaces; +using xFileService.Models.Dtos; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; +using System.Collections.Generic; +using xFileService.Providers.Dtos; +using xIdentityService.Interfaces; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces +{ + public interface IXFileProvider : IXBaseProvidedHasTag, IXBaseReferenced + { + // + #region Tools ... + /// + /// Stream Specified File ... + /// + /// + /// + /// + Task Stream( + Guid id, + CancellationToken cancellationToken = default + ); + + /// + /// Stream Specified File ... + /// + /// + /// + /// + Task Stream( + string name, + CancellationToken cancellationToken = default + ); + + /// + /// Download Specified File ... + /// + /// + /// + /// + Task Download( + Guid id, + CancellationToken cancellationToken = default + ); + + /// + /// Download Specified File ... + /// + /// + /// + /// + Task Download( + string name, + CancellationToken cancellationToken = default + ); + + /// + /// Upload Files ... + /// + /// + /// + /// + /// + /// + Task> Upload( + IFormFileCollection files, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Specified Files ... + /// + /// + /// + /// + /// + /// + Task> Remove( + string ids, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Specified File Streaming Info ... + /// + /// + /// + /// + Task GetFileDescriptor( + Guid id, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Specified File Streaming Info ... + /// + /// + /// + /// + Task GetFileDescriptor( + string fileName, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\IXFileProviderController.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using xTagService.Interfaces; +using Microsoft.AspNetCore.Mvc; +using xFileService.Models.Dtos; +using Microsoft.AspNetCore.Http; +using System.Collections.Generic; +using xFileService.Providers.Dtos; +using xIdentityService.Interfaces; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces +{ + public interface IXFileProviderController : IXBaseProvidedHasTagController, IXBaseReferencedController + { + // + #region Tools ... + /// + /// Stream Specified File ... + /// + /// + /// + /// + [HttpGet("{id}/Stream/ById")] + Task Stream( + [FromRoute] Guid id, + CancellationToken cancellationToken = default + ); + + /// + /// Stream Specified File ... + /// + /// + /// + /// + [HttpGet("{name}/Stream/ByName")] + Task Stream( + [FromRoute] string name, + CancellationToken cancellationToken = default + ); + + /// + /// Download Specified File ... + /// + /// + /// + /// + [HttpGet("{id}/Download/ById")] + Task Download( + [FromRoute] Guid id, + CancellationToken cancellationToken = default + ); + + /// + /// Download Specified File ... + /// + /// + /// + /// + [HttpGet("{name}/Download/ByName")] + Task Download( + string name, + CancellationToken cancellationToken = default + ); + + /// + /// Upload Files ... + /// + /// + /// + /// + [HttpPost("Upload")] + [RequestSizeLimit(966_367_641)] + Task>> Upload( + [FromForm] IFormFileCollection files, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Specified Files ... + /// + /// + /// + /// + [HttpDelete("")] + Task>> Remove( + [FromQuery] string ids, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\IXFileTagProvided.cs + +using System; +using xTagService.Interfaces; +using xFileService.Models.Dtos; +using xFileService.Providers.Dtos; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces +{ + public interface IXFileTagProvided : IXTagProvided + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\IXFileTagProvider.cs + +using System; +using xTagService.Interfaces; + +namespace xFileService.Interfaces +{ + public interface IXFileTagProvider : IXBaseTagProvider + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Dtos\IXFileDtoHub.cs + +using System; +using xPushService.Interfaces; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces.Dtos +{ + public interface IXFileDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Dtos\IXFileRepositoryService.cs + +using System; +using xDataService.Interfaces; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces.Dtos +{ + public interface IXFileRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Dtos\IXFileServiceProvider.cs + +using System; +using xPushService.Interfaces; +using xFileService.Models.Dtos; +using xFileService.Providers.Dtos; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces.Dtos +{ + public interface IXFileServiceProvider : IXBaseDtoProvider + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Entities\IXFileEntityHub.cs + +using System; +using xPushService.Interfaces; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces.Entities +{ + public interface IXFileEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Entities\IXFileGraphQLTypeHelper.cs + +using System; +using xDataService.Interfaces; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces.Entities +{ + public interface IXFileGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Entities\IXFileKeyGenerator.cs + +using System; +using xDataService.Interfaces; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces.Entities +{ + public interface IXFileKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Entities\IXFileRepository.cs + +using System; +using xDataService.Interfaces; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces.Entities +{ + public interface IXFileRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Entities\IXFileRepositoryEvents.cs + +using xDataService.Interfaces; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces.Entities +{ + public interface IXFileRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Entities\IXFileRepositoryProvider.cs + +using System; +using xPushService.Interfaces; +using xFileService.Models.Entities; +using xFileService.Providers.Entities; + +namespace xFileService.Interfaces.Entities +{ + public interface IXFileRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Entities\IXFileSeeder.cs + +using System; +using xDataService.Interfaces; +using xFileService.Models.Entities; + +namespace xFileService.Interfaces.Entities +{ + public interface IXFileSeeder : IXBaseDbSeeder + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Models\Dtos\XFileDto.cs + +using System; +using xModels.Base; +using xModels.Dtos; +using xCommons.Constants; + +namespace xFileService.Models.Dtos +{ + public class XFileDto : XBaseGuidIDEntityDto + { + public XFileType Type { get; set; } + public string Name { get; set; } + public string Thumb { get; set; } + public string Path { get; set; } + public string ThumbPath { get; set; } + public DateTime UploadedOn { get; set; } + public string FileName { get; set; } + + public string OwnerId { get; set; } + public XPersonDto Owner { get; set; } + + public string References { get; set; } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Models\Dtos\XFileStreamDescriptorDto.cs + +using System.IO; +using xModels.Base; + +namespace xFileService.Models.Dtos +{ + public class XFileStreamDescriptorDto : XBaseDto + { + public Stream Stream { get; set; } + public string MIMEType { get; set; } + public string Name { get; set; } + public string Path { get; set; } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Models\Entities\XFile.cs + +using System; +using xModels.Base; +using xCommons.Constants; +using System.ComponentModel.DataAnnotations; + +namespace xFileService.Models.Entities +{ + /// + /// Specified File in Server ... + /// + public class XFile : XBaseGuidIDEntity + { + /// + /// Specified File Type ... + /// + /// + public XFileType Type { get; set; } + + /// + /// User Identifier ... + /// + /// + [Required] + [StringLength(255)] + public string OwnerId { get; set; } + + /// + /// File Name on Server ... + /// + /// + [Required] + [StringLength(255)] + public string Name { get; set; } + + /// + /// File Path On Server ... + /// + /// + [Required] + public string Path { get; set; } + + /// + /// Thumbnail FIle Name ... + /// if File Type Supported Thumbnail ... + /// + /// + public string Thumb { get; set; } + + /// + /// Thumbnail File Path ... + /// if File Type Supported Thumbnail ... + /// + /// + public string ThumbPath { get; set; } + + /// + /// File Uploading Time ... + /// + /// + public DateTime UploadedOn { get; set; } + + /// + /// Request For Upload File Name on Client ... + /// + /// + public string FileName { get; set; } + + /// + /// Comma Separated List Which Provides Pointer to Specified + /// Model for Describe ... + /// + /// + public string References { get; set; } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\XFileEntityRegisterar.cs + +using System; +using xDataService.Providers; +using xDataService.Interfaces; +using xFileService.Models.Entities; +using Microsoft.EntityFrameworkCore; +using xFileService.Configurations.Entities; + +namespace xFileService.Providers +{ + public class XFileEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar + { + public XFileEntityRegisterar() + : base(nameof(XFileEntityRegisterar)) + { + // + // Add XFile Entity ... + AddEntity(); + } + + /// + /// Configure Entities ... + /// + /// + public override void ConfigureEntities(ModelBuilder modelBuilder) + { + // + // Configure Entity Using Provided ... + modelBuilder + .ApplyConfigurationsFromAssembly(typeof(XFileEntityConfiguration).Assembly); + + // + base.ConfigureEntities(modelBuilder); + } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\XFileProvider.cs + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using xCommons.Extensions; +using xDataService.Models; +using xExceptions.Constants; +using xTagService.Providers; +using System.Threading.Tasks; +using xIdentityModels.Models; +using xFileService.Constants; +using xFileService.Extensions; +using xFileService.Interfaces; +using Microsoft.AspNetCore.Mvc; +using xFileService.Models.Dtos; +using Microsoft.AspNetCore.Http; +using System.Collections.Generic; +using xStorageService.Interfaces; +using xFileService.Providers.Dtos; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Entities; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Extensions.FileProviders; + +namespace xFileService.Providers +{ + public class XFileProvider : XBaseProvidedHasTag, IXFileProvider + { + private readonly IXFileServiceProvider provider; + private readonly IXStorageProvider storageProvider; + + public XFileProvider( + IXFileTagProvider tagProvider, + IXFileServiceProvider provider, + IXStorageProvider storageProvider + ) : base( + provider: provider, + tagProvider: tagProvider, + permittedRoles: [XFileServiceConstants.XAdminRole] + ) + { + this.provider = provider; + this.storageProvider = storageProvider; + } + + public override bool IsOwned( + XFile item, + XUserClaimsInfoDto userInfo + ) + { + // + var result = + !item.IsNullOrDefault() && + !userInfo.IsNullOrDefault() && + !item.OwnerId.IsNullOrEmpty() && + userInfo.UserId == item.OwnerId; + + // + return result; + } + + public override bool IsOwned( + XFileDto item, + XUserClaimsInfoDto userInfo + ) + { + // + var result = + !item.IsNullOrDefault() && + !userInfo.IsNullOrDefault() && + !item.OwnerId.IsNullOrEmpty() && + userInfo.UserId == item.OwnerId; + + // + return result; + } + + // + #region Tools ... + /// + /// Stream Specified File ... + /// + /// + /// + /// + public virtual async Task Stream( + Guid id, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + var isValid = !id.IsNull() && + !id.IsDefaultGuid(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Generate Stream Info and Validate it ... + var stream = await GetFileDescriptor( + id: id, + cancellationToken: cancellationToken + ); + if (stream.IsNull()) + { + XException.NotFound.Throw(); + } + + // + // Generate Result Model ... + var result = new FileStreamResult( + stream.Stream, + stream.MIMEType + ) + { + FileDownloadName = stream.Name + }; + + // + return result; + } + + /// + /// Stream Specified File ... + /// + /// + /// + /// + public virtual async Task Stream( + string name, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + var isValid = !name.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Generate Stream Info and Validate it ... + var stream = await GetFileDescriptor( + fileName: name, + cancellationToken: cancellationToken + ); + if (stream.IsNull()) + { + XException.NotFound.Throw(); + } + + // + // Generate Result Model ... + var result = new FileStreamResult( + stream.Stream, + stream.MIMEType + ) + { + FileDownloadName = stream.Name + }; + + // + return result; + } + + /// + /// Download Specified File ... + /// + /// + /// + /// + public virtual async Task Download( + Guid id, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + var isValid = !id.IsNull() && + !id.IsDefaultGuid(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Generate Stream Info and Validate it ... + var stream = await GetFileDescriptor( + id: id, + cancellationToken: cancellationToken + ); + if (stream.IsNull()) + { + XException.NotFound.Throw(); + } + + // + // Generate Result Model ... + var result = new PhysicalFileResult( + stream.Path, + stream.MIMEType + ) + { + FileDownloadName = stream.Name + }; + + // + return result; + } + + /// + /// Download Specified File ... + /// + /// + /// + /// + public virtual async Task Download( + string name, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + var isValid = !name.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Generate Stream Info and Validate it ... + var stream = await GetFileDescriptor( + fileName: name, + cancellationToken: cancellationToken + ); + if (stream.IsNull()) + { + XException.NotFound.Throw(); + } + + // + // Generate Result Model ... + var result = new PhysicalFileResult( + stream.Path, + stream.MIMEType + ) + { + FileDownloadName = stream.Name + }; + + // + return result; + } + + /// + /// Upload Files ... + /// + /// + /// + /// + /// + /// + public virtual async Task> Upload( + IFormFileCollection files, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + var isValid = !files.IsNull() && + files.HasChild() && + !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Save Files On Server Side Physically ... + // and Validate it ... + var uploadResults = await storageProvider + .HandleFilesSave(files); + isValid = uploadResults.HasChild(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + + // + // Prepare Result ... + var result = new List(); + foreach (var ur in uploadResults) + { + // + var fileType = storageProvider.GetFileType(ur.FileName); + + // + // Create Item Model ... + var item = new XFileDto + { + Type = fileType, + FileName = ur.Name, + Name = ur.FileName, + Path = ur.FilePath, + Thumb = ur.Thmbnail, + OwnerId = userInfo.UserId, + ThumbPath = ur.ThmbnailPath, + UploadedOn = DateTime.UtcNow, + }; + + // + try + { + // + // Add Entity Model to DB ... + item = await Add( + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + result.Add(item); + } + catch { } + } + + // + return result; + } + + /// + /// Remove Specified Files ... + /// + /// + /// + /// + /// + /// + public virtual async Task> Remove( + string ids, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + var isValid = !ids.IsNullOrEmpty() && + !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Parse ID(s) List ... + var idsList = ids.ParseListGuid(); + isValid = !idsList.IsNull() && + idsList.HasChild(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + + // + // Loop Through Parsed Ids and Try to Remove ... + var result = new List(); + foreach (var id in idsList) + { + // + // Retrieve XFileDto and Validate it ... + var model = await Get(id); + isValid = !model.IsNull() && !model.Id.IsNull() && !model.Id.IsDefaultGuid(); + if (!isValid) + { + continue; + } + + // + // Check Physical File Exists or not ... + var fileFullPath = storageProvider.GetFileFullPath(model.Name); + var thumbFullPath = model.Thumb.IsNullOrEmpty() + ? "" + : storageProvider.GetThumbnailFileFullPath(model.Thumb); + var isFileExists = storageProvider.IsFileExists(fileFullPath); + var isThumbExists = model.Thumb.IsNullOrEmpty() + ? false + : storageProvider.IsFileExists(thumbFullPath); + + // + // Remove Physical Thumbnail File ... + if (isThumbExists) + { + // + try + { + // + // Removed Stored Thumbnail ... + storageProvider.DeleteFile( + fileFullPath: thumbFullPath, + forceFileExists: false + ); + } + catch { } + } + + // + // Remove Pgysical File ... + if (isFileExists) + { + // + try + { + // + // Remove Stored File ... + storageProvider.DeleteFile( + fileFullPath: fileFullPath, + forceFileExists: false + ); + + // + // Remove Tags ... + await DetachTags( + id: id, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + catch { } + } + + // + // Remove File Entty ... + var item = await provider.RemoveAsync( + id: model.Id, + softDelete: false, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + result.Add(model.Id); + } + + // + return result; + } + + /// + /// Retrieve Specified File Streaming Info ... + /// + /// + /// + /// + public virtual async Task GetFileDescriptor( + Guid id, + CancellationToken cancellationToken = default + ) + { + // + // Check File Model Exists or not ... + var isExists = await IsExists( + id: id, + cancellationToken: cancellationToken + ); + if (!isExists) + { + XException.NotFound.Throw(); + } + + // + // Retrieve Item and Validate it ... + var item = await Get( + id: id, + includeBuilder: null, + cancellationToken: cancellationToken + ); + if (item.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + // Generate Physical File Path and Validate it ... + var fileFullPath = storageProvider.GetFileFullPath(item.Name); + isExists = storageProvider.IsFileExists(fileFullPath); + var filePath = storageProvider.FilePathResolver(fileFullPath); + filePath = Path.Combine("wwwroot", filePath); + if (!isExists) + { + XException.NotFound.Throw(); + } + + // + // Generate Physical File Info ... + IFileInfo fileInfo = storageProvider.FileProvider.GetFileInfo(filePath); + + // + // Prepare Default Streaming Requirements ... + var mimeType = "application/octetstream"; + var readStream = fileInfo.CreateReadStream(); + var fileMimeProvider = new FileExtensionContentTypeProvider(); + var fileName = item.Name; + + // + // Try to Find Physical File's Content Type ... + fileMimeProvider.TryGetContentType( + filePath, + out mimeType + ); + + // + // Prepare Result Model ... + var result = new XFileStreamDescriptorDto + { + Stream = readStream, + MIMEType = mimeType, + Name = fileName, + Path = fileFullPath + }; + + // + return result; + } + + /// + /// Retrieve Specified File Streaming Info ... + /// + /// + /// + /// + public virtual async Task GetFileDescriptor( + string fileName, + CancellationToken cancellationToken = default + ) + { + // + if (fileName.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + var item = await FindOne( + includeBuilder: null, + predicate: e => + e.Name.Contains(fileName) || + e.Path.Contains(fileName) || + e.Thumb.Contains(fileName) || + e.FileName.Contains(fileName) || + e.ThumbPath.Contains(fileName), + cancellationToken: cancellationToken + ); + var isExists = !item.IsNullOrDefault(); + if (!isExists) + { + XException.NotFound.Throw(); + } + + // + // Extract File Name ... + fileName = Path.GetFileName(fileName); + + // + // Check File is Thumbnail or File ... + var isThumb = fileName.StartsWith(storageProvider.Configuration.ThumbPrefix); + var isFile = fileName.StartsWith(storageProvider.Configuration.FilePrefix); + + // + // Check File Type is Valid ... + isExists = isThumb || isFile; + if (!isExists) + { + XException.UnsupportedFileType.Throw(); + } + + // + // Extract File Full Path ... + var fileFullPath = ""; + if (isThumb) + { + fileFullPath = storageProvider.GetThumbnailFileFullPath(fileName); + } + else + { + fileFullPath = storageProvider.GetFileFullPath(fileName); + } + + // + // Check Physical File Exists or Not ... + isExists = storageProvider.IsFileExists(fileFullPath); + if (!isExists) + { + XException.NotFound.Throw(); + } + + // + // Generate Physical File Info ... + var filePath = storageProvider.FilePathResolver(fileFullPath); + filePath = Path.Combine("wwwroot", filePath); + IFileInfo fileInfo = storageProvider.FileProvider.GetFileInfo(filePath); + + // + // Prepare Default Streaming Requirements ... + var mimeType = "application/octetstream"; + var readStream = fileInfo.CreateReadStream(); + var fileMimeProvider = new FileExtensionContentTypeProvider(); + + // + // Try to Find Physical File's Content Type ... + fileMimeProvider.TryGetContentType(filePath, out mimeType); + + // + // Prepare Result Model ... + var result = new XFileStreamDescriptorDto + { + Stream = readStream, + MIMEType = mimeType, + Name = fileName, + Path = fileFullPath + }; + + // + return result; + } + #endregion + + // + #region Referenced Actions ... + /// + /// Get Reference Identifier for Specified Provider and Specified Item ... + /// + /// + /// + /// + public string GetIdentifier( + string providedFor, + TReferenceKey forProvidedId + ) + { + // + var result = $"{providedFor}_{forProvidedId}"; + + // + return result; + } + + /// + /// Check Specified Item has Refernce to Provider ... + /// + /// + /// + /// + /// + /// + public async Task IsProvidedFor( + string providedFor, + TReferenceKey forProvidedId, + Guid id, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull() && + !id.IsDefaultGuid() && + !forProvidedId.IsNull() && + !providedFor.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Exists ... + isValid = await IsExists( + id: id, + cancellationToken: cancellationToken + ); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + var item = await Get( + id: id, + includeBuilder: null, + cancellationToken: cancellationToken + ); + isValid = !item.IsNullOrDefault(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + + // + var references = item.GetReferences(); + var result = + references.IsNull() && + references.HasChild() && + references.Any(r => + r.ProvidedFor == providedFor && + r.ReferencedTo == $"{forProvidedId}" + ); + + // + return result; + } + + /// + /// Add Specified Reference to Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + public async Task AddReference( + string providedFor, + TReferenceKey forProvidedId, + Guid id, + int forIndex = 0, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !providedFor.IsNullOrEmpty() && + !id.IsNull() && + !id.IsDefaultGuid() && + !providedFor.IsNull() && + !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Has Reference or not ... + isValid = await IsProvidedFor( + id: id, + providedFor: providedFor, + forProvidedId: forProvidedId, + cancellationToken: cancellationToken + ); + if (isValid) + { + // + // Prevent Moving Forward Since Reference Exists ... + return; + } + + // + // Check Permissions ... + var item = await Get( + id: id, + includeBuilder: null, + cancellationToken: cancellationToken + ); + isValid = + !item.IsNullOrDefault() && + (IsOwned(item, userInfo) || + HasPermision(userInfo)); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + var reference = new XReference + { + Index = forIndex, + ProvidedFor = providedFor, + ReferencedTo = $"{forProvidedId}" + }; + var references = item.GetReferences(); + references.Add(reference); + item = item.UpdateReferences(references); + item = await Update( + id: id, + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Remove Specified Reference from Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + public async Task RemoveReference( + string providedFor, + TReferenceKey forProvidedId, + Guid id, + int forIndex = 0, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull() && + !id.IsDefaultGuid() && + !forProvidedId.IsNull() && + !userInfo.IsNullOrDefault() && + !providedFor.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Has Reference or not ... + isValid = await IsProvidedFor( + id: id, + providedFor: providedFor, + forProvidedId: forProvidedId, + cancellationToken: cancellationToken + ); + if (!isValid) + { + // + // Prevent Moving Forward Since Reference not Exists ... + return; + } + + // + // Check Permissions ... + var item = await Get( + id: id, + includeBuilder: null, + cancellationToken: cancellationToken + ); + isValid = + !item.IsNullOrDefault() && + (IsOwned(item, userInfo) || + HasPermision(userInfo)); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + var identifier = GetIdentifier( + providedFor: providedFor, + forProvidedId: forProvidedId + ); + var references = item.GetReferences(); + references = references + .Where(r => + r.Index != forIndex && + r.ProvidedFor != providedFor && + r.ReferencedTo != $"{forProvidedId}" + ) + .ToList(); + item = item.UpdateReferences(references); + + // + item = await Update( + item: item, + id: item.Id, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Get Specified References ... + /// + /// + /// + /// + /// + public async Task>> GetReferences( + string providedFor, + Guid id, + CancellationToken cancellationToken = default + ) + { + // + IList> result = new List>(); + + // + var item = await provider.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + var isValid = !item.IsNullOrDefault(); + if (!isValid) + { + return result; + } + + // + result = item.GetReferences(); + + // + result = result.Where(r => + r.ProvidedFor == providedFor + ) + .ToList(); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\XFileTagProvided.cs + +using System; +using xCommons.Extensions; +using xTagService.Providers; +using xIdentityModels.Models; +using xFileService.Constants; +using xFileService.Interfaces; +using xFileService.Models.Dtos; +using xFileService.Providers.Dtos; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Entities; + +namespace xFileService.Providers +{ + public class XFileTagProvided : XBaseTagProvided, IXFileTagProvided + { + public XFileTagProvided( + IXFileTagProvider tagProvider, + IXFileServiceProvider provider + ) : base( + provider: provider, + tagProvider: tagProvider, + permittedRoles: [XFileServiceConstants.XAdminRole] + ) + { } + + public override bool IsOwned( + XFileDto item, + XUserClaimsInfoDto userInfo + ) + { + // + var result = + !item.IsNullOrDefault() && + !userInfo.IsNullOrDefault() && + item.OwnerId == userInfo.UserId; + + // + return result; + } + + public override bool IsOwned( + XFile item, + XUserClaimsInfoDto userInfo + ) + { + // + var result = + !item.IsNullOrDefault() && + !userInfo.IsNullOrDefault() && + item.OwnerId == userInfo.UserId; + + // + return result; + } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\XFileTagProvider.cs + +using System; +using xTagService.Providers; +using xFileService.Constants; +using xFileService.Interfaces; +using xTagService.Interfaces.Dtos; + +namespace xFileService.Providers +{ + public class XFileTagProvider : XBaseTagProvider, IXFileTagProvider + { + public XFileTagProvider( + IXTagServiceProvider tagProvider + ) : base( + tagProvider: tagProvider, + providedFor: XFileServiceConstants.XFileTagProvided + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Dtos\XFileDtoHub.cs + +using System; +using xPushService.Base; +using xFileService.Models.Dtos; +using Microsoft.Extensions.Logging; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Entities; + +namespace xFileService.Providers.Dtos +{ + public class XFileDtoHub : XBaseDtoHub, IXFileDtoHub + { + public XFileDtoHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Dtos\XFileRepositoryService.cs + +using System; +using AutoMapper; +using xDataService.Providers; +using xFileService.Models.Dtos; +using System.Collections.Generic; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.Dtos +{ + public class XFileRepositoryService : XBaseRepositoryService, IXFileRepositoryService + { + public XFileRepositoryService( + IXFileRepository repository, + IEnumerable mapperProfiles = null + ) : base( + repository, + mapperProfiles + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Dtos\XFileServiceProvider.cs + +using System; +using xPushService.Base; +using xFileService.Models.Dtos; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using Microsoft.AspNetCore.SignalR; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Entities; + +namespace xFileService.Providers.Dtos +{ + public class XFileServiceProvider : XBaseDtoProvider, IXFileServiceProvider + { + public XFileServiceProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXFileRepositoryService service, + IXIdentityProvider identityProvider = null + ) : base( + hub, + dataConfiguration, + service, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Entities\XFileEFRepository.cs + +using System; +using xDataService.Db; +using xDataService.Providers; +using xDataService.Interfaces; +using xDataService.Configuration; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.Entities +{ + public class XFileEFRepository : XBaseEFRepository, IXFileRepository + where TContext : XDbContext + { + public XFileEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXFileKeyGenerator keyGenerator = null, + IXFileRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Entities\XFileEntityHub.cs + +using System; +using xPushService.Base; +using Microsoft.Extensions.Logging; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.Entities +{ + public class XFileEntityHub : XBaseEntityHub, IXFileEntityHub + { + public XFileEntityHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Entities\XFileInMemoryRepository.cs + +using System; +using xDataService.Providers; +using xDataService.Configuration; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.Entities +{ + public class XFileInMemoryRepository : XBaseInMemoryRepository, IXFileRepository + { + public XFileInMemoryRepository( + XDataServiceConfiguration configuration, + IXFileKeyGenerator keyGenerator = null, + IXFileRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Entities\XFileKeyGenerator.cs + +using xDataService.Providers; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.Entities +{ + public class XFileKeyGenerator : XGuidKeyGenerator, IXFileKeyGenerator + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Entities\XFileMongoRepository.cs + +using System; +using xDataService.Providers; +using xDataService.Configuration; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.Entities +{ + public class XFileMongoRepository : XBaseMongoRepository, IXFileRepository + { + public XFileMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXFileKeyGenerator keyGenerator = null, + IXFileRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Entities\XFileRepositoryEvents.cs + +using xDataService.Providers; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.Entities +{ + public class XFileRepositoryEvents : XBaseRepositoryEvents, IXFileRepositoryEvents + { } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Entities\XFileRepositoryProvider.cs + +using System; +using xPushService.Base; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using Microsoft.AspNetCore.SignalR; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.Entities +{ + public class XFileRepositoryProvider : XBaseEntityProvider, IXFileRepositoryProvider + { + public XFileRepositoryProvider( + IHubContext hub, + IXFileRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + repository, + dataConfiguration, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\Entities\XFileSeederBase.cs + +using System; +using xDataService.Providers; +using xDataService.Configuration; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.Entities +{ + public abstract class XFileSeederBase : XBaseDbSeeder, IXFileSeeder + { + protected XFileSeederBase( + string entity, + string database, + IXFileRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + entity, + database, + repository, + dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\GraphQL\XFileGraphQLTypeHelper.cs + +using System; +using xDataService.GraphQL; +using xFileService.Constants; +using xDataService.Configuration; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.GraphQL +{ + public class XFileGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXFileGraphQLTypeHelper + { + public XFileGraphQLTypeHelper( + XDataServiceConfiguration configuration + ) : base(configuration) + { } + + public override string GetInQueryCollectionName() + { + return XFileServiceConstants.XFileCollectionName; + } + + public override string GetInQuerySingleName() + { + return XFileServiceConstants.XFileSingleName; + } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\GraphQL\XFileGraphQuery.cs + +using System; +using GraphQL.Types; +using xDataService.GraphQL; +using xDataService.Configuration; +using xFileService.Models.Entities; +using xFileService.Interfaces.Entities; + +namespace xFileService.Providers.GraphQL +{ + public class XFileGraphQuery : XBaseGraphQLQuery + { + public XFileGraphQuery( + XDataServiceConfiguration configuration, + IXFileRepository repository, + IXFileGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\GraphQL\XFileGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xFileService.Providers.GraphQL +{ + public class XFileGraphSchema : Schema + { + public XFileGraphSchema(IServiceProvider services) : base(services) + { + Query = (XFileGraphQuery)services.GetService(typeof(XFileGraphQuery)); + } + + } +} + +### FILE: C:\Users\saherelm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\GraphQL\XFileGraphType.cs + +using System; +using xDataService.GraphQL; +using xFileService.Models.Entities; + +namespace xFileService.Providers.GraphQL +{ + public class XFileGraphType : XBaseGraphObjectType + { + public XFileGraphType() : base() + { + Field(x => x.Type); + Field(x => x.Name); + Field(x => x.Path); + Field(x => x.Thumb); + Field(x => x.OwnerId); + Field(x => x.FileName); + Field(x => x.ThumbPath); + Field(x => x.UploadedOn); + Field(x => x.References); + } + } +} diff --git a/Modules/xAiModels b/Modules/xAiModels index 47b2cbc..755eafa 160000 --- a/Modules/xAiModels +++ b/Modules/xAiModels @@ -1 +1 @@ -Subproject commit 47b2cbc0d2366ff13f980081edde9f4c7abdc43e +Subproject commit 755eafa7d2b8c1dfcd2d0da3da9f34d736c211c3 diff --git a/Modules/xAiService b/Modules/xAiService index 3a7cec7..a7e874d 160000 --- a/Modules/xAiService +++ b/Modules/xAiService @@ -1 +1 @@ -Subproject commit 3a7cec74e1f2d0e1abad8a530149b39b85d8b7e3 +Subproject commit a7e874d4784e5e1199ddc0a44471feacfb32e5b6 diff --git a/xAiApi b/xAiApi index efaf71d..aac3eb9 160000 --- a/xAiApi +++ b/xAiApi @@ -1 +1 @@ -Subproject commit efaf71d9629f059ff56977f0ccce325ab693031f +Subproject commit aac3eb92c12775ed40b53946785e43435f741b87 diff --git a/xSaherelmWorkspace.code-workspace b/xSaherelmWorkspace.code-workspace index 876a149..4dcc0d4 100644 --- a/xSaherelmWorkspace.code-workspace +++ b/xSaherelmWorkspace.code-workspace @@ -4,5 +4,7 @@ "path": "." } ], - "settings": {} + "settings": { + "dotnet.defaultSolution": "xSaherelmWorkspace.sln" + } } \ No newline at end of file