using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using xCommons.Extensions; using xDataService.Extensions; using xDataService.Models; using xExceptions.Constants; using xFileService.Interfaces; using xFileService.Models.Dtos; using xIdentityModels.Models; using xModels.Dtos; using XFileDto = xFileService.Models.Dtos.XFileDto; namespace xFileService.Providers { public abstract class XBaseFileProvider : IXBaseFileProvider { // #region Props ... /// /// Provider Identifier ... /// public string Provider { get; } /// /// Tag Provider for Maipulating Tags ... /// public IXFileProvider FileProvider { get; } #endregion // #region Constructor ... public XBaseFileProvider( string providedFor, IXFileProvider fileProvider ) { // Provider = providedFor; FileProvider = fileProvider; } #endregion // #region Tools ... /// /// Stream Specified File ... /// /// /// public async Task Stream(Guid id) { // // Check Validation and Owning ... var isValid = await IsOwned(id); if (!isValid) { XException.NotAllowed.Throw(); } // var result = await FileProvider.Stream(id); return result; } /// /// Stream Specified File ... /// /// /// public async Task Stream(string fileName) { // // Validate ... var isValid = !fileName.IsNullOrEmpty(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Retrieve Dto and also Checking Owned ... var dto = await GetDto(fileName); isValid = !dto.IsNullOrDefault(); if (!isValid) { XException.NotAllowed.Throw(); } // var result = await FileProvider.Stream(fileName); return result; } /// /// Download Specified File ... /// /// /// public async Task Download(Guid id) { // // Validate ... var isValid = await IsOwned(id); if (!isValid) { XException.NotAllowed.Throw(); } // var result = await FileProvider.Download(id); return result; } /// /// Download Specified File ... /// /// /// public async Task Download(string fileName) { // // Validate ... var isValid = !fileName.IsNullOrEmpty(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Retrieve Dto and also Checking Owned ... var dto = await GetDto(fileName); isValid = !dto.IsNullOrDefault(); if (!isValid) { XException.NotAllowed.Throw(); } // var result = await FileProvider.Download(fileName); return result; } /// /// Upload Files ... /// /// /// /// /// /// public async Task> Upload( TKey providedForId, IFormFileCollection files, string connectionId = null, XUserClaimsInfoDto userInfo = null ) { // // Validate ... var isValid = !providedForId.IsNull(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Here we are Following Custom Senario ... var dtos = await FileProvider.Upload( files: files, userInfo: userInfo, connectionId: connectionId ); // // Validate Dtos ... isValid = !dtos.IsNull() && dtos.HasChild(); if (isValid) { // var isReferenced = false; foreach (var dto in dtos) { // // Try to Add Reference to Specified DTO ... isReferenced = await AddReference( model: dto, userInfo: userInfo, connectionId: connectionId, providedForId: providedForId ); } } // var result = new List(); // isValid = !dtos.IsNull() && dtos.HasChild(); if (isValid) { // foreach (var dto in dtos) { // var idto = await FileProvider.Get(dto.Id); isValid = !idto.IsNullOrDefault() && await IsOwned(idto.Id); if (isValid) { result.Add(idto); } } } // return result; } /// /// Retrieve Specified File Streaming Info ... /// /// /// public async Task GetFileDescriptor(Guid id) { // // Validate ... var isValid = !id.IsNull() && !id.IsDefaultGuid(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Check Owned ... isValid = await IsOwned(id); if (!isValid) { XException.NotAllowed.Throw(); } // var result = await FileProvider.GetFileDescriptor(id); return result; } /// /// Retrieve Specified File Streaming Info ... /// /// /// public async Task GetFileDescriptor(string fileName) { // // Validate ... var isValid = !fileName.IsNullOrEmpty(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Check Owned ... var dto = GetDto(fileName); isValid = !dto.IsNullOrDefault(); if (!isValid) { XException.NotAllowed.Throw(); } // var result = await FileProvider.GetFileDescriptor(fileName); return result; } #endregion // #region Referenced Actions ... /// /// Get Specified Indexed Reference for Specific Provided ID ... /// /// /// /// public async Task GetReference( TKey providedForId, int forIndex = 0 ) { // // Validate ... if (providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // // Normalize ... if (forIndex < 0) { forIndex = 0; } // // Retrieve Last Index ... var maxIndex = (await GetReferencingIndexes(providedForId)).Max(); if (forIndex > maxIndex) { XException.NotFound.Throw(); } // var indexedIdentifier = GetIndexedProvidedID( forIndex: forIndex, providedForId: providedForId ); var entity = FileProvider.FileRepository .AsQueryable() .Where(t => t.References.Contains(indexedIdentifier)) .FirstOrDefault(); if (entity.IsNullOrDefault()) { XException.NotFound.Throw(); } // var result = await FileProvider.ToDto(entity); return result; } /// /// Retrieve all Exists References of Specific Provided ID ... /// /// /// public async Task> GetAllReferences(TKey providedForId) { // // Validate ... if (providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // var identifier = GetProvidedID(providedForId); var entities = FileProvider.FileRepository .AsQueryable() .Where(t => t.References.Contains(identifier)) .AsEnumerable(); // var result = new List(); foreach (var entity in entities) { // var dto = await FileProvider.ToDto(entity); if (!dto.IsNullOrDefault()) { result.Add(dto); } } // return await Task.FromResult(result); } /// /// Extract all Referencing Models for Specified Key ... /// /// /// public async Task>> GetAllReferencings(TKey providedForId) { // // Validate ... var isValid = !providedForId.IsNull(); if (!isValid) { XException.InvalidArgs.Throw(); } // var identifier = GetProvidedID(providedForId); // var result = FileProvider.FileRepository .AsQueryable() .Where(te => te.References.Contains(identifier)) .Select(te => te.References) .ToList() .SelectMany(tr => tr.ParseXReferenceList()) .Where(tr => tr.ProvidedFor == Provider && $"{tr.ReferencedTo}" == $"{providedForId}") .OrderBy(tr => tr.Index) .AsEnumerable() ; // return await Task.FromResult(result); } /// /// Count References ... /// /// /// public async Task CountReferences(TKey providedForId) { // // Validate ... if (providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // var identifier = GetProvidedID(providedForId); var result = FileProvider.FileRepository .AsQueryable() .Where(t => t.References.Contains(identifier)) .Count(); // return await Task.FromResult(result); } /// /// Retrieve References of Specific Provided ID as Query ... /// /// /// public async Task> QueryReferences( XQuery query, TKey providedForId ) { // // Validate ... if (query.IsNull() || providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // // Normalize ... query = query.NormalizeQuery(FileProvider.DataSercviceConfiguration); // var items = await GetAllReferences(providedForId); var totalItemsCount = items.Count(); // // Apply Filter ... if (!query.Filter.IsNullOrEmpty()) { // items = items .ApplyFilter(query.Filter); } int filteredItemsCount = items.Count(); // // Count Pages ... var totalPagesCount = query.CountPages(totalItemsCount); var filteredPagesCount = query.CountPages(filteredItemsCount); // // Apply Paging and Sorting ... if (totalItemsCount > 0 && filteredItemsCount > 0) { // // Apply Sorting ... items = items .ToList() .ApplySorting( query.SortBy, query.IsAscending ); // // Apply Paging ... items = items .ToList() .ApplyPaging( query.Page, query.PageSize ); } // // Prepare Result ... var result = new XQueryResult { Page = query.Page, Items = items.ToList(), PageSize = query.PageSize, TotalPages = totalPagesCount, TotalItems = totalItemsCount, TotalFilteredPages = filteredPagesCount, TotalFilteredItems = filteredItemsCount }; // return result; } /// /// Add Reference a Model to Specific Provided ID ... /// /// /// /// /// /// /// public async Task AddReference( XFileDto model, TKey providedForId, string connectionId = null, XUserClaimsInfoDto userInfo = null ) { // var result = false; // // Validate ... result = !model.Id.IsNull() && !providedForId.IsNull() && !model.IsNullOrDefault() && !model.Id.IsDefaultGuid() && !model.Name.IsNullOrEmpty(); if (!result) { XException.InvalidArgs.Throw(); } // // Check Model Exists ... result = await FileProvider.IsExists(model.Id); if (!result) { XException.NotFound.Throw(); } // // Update Model by Retrieving ... model = await FileProvider.Get(model.Id); // // Retrieve all References ... var references = await GetAllReferences(providedForId); // // Check isReferenced or not ... result = references.Any(r => r.Id == model.Id && r.References.Any(rf => rf.ProvidedFor == Provider && rf.ReferencedTo == $"{providedForId}")); if (result) { return result; } // int forIndex = await CountReferences(providedForId); model.References.Add(new XReference { Index = forIndex, ProvidedFor = Provider, ReferencedTo = $"{providedForId}" }); // // Update Data Base ... model = await FileProvider .Update( item: model, id: model.Id, userInfo: userInfo, connectionId: connectionId ); result = !model.IsNullOrDefault(); // return result; } /// /// Add Reference a Model to Specific XRefence ... /// /// /// /// /// /// public async Task AddReference( XFileDto model, XReference reference, string connectionId = null, XUserClaimsInfoDto userInfo = null ) { // // Validate ... // Since other Validations Handled in Calling, we Ignore them here ... var result = !model.IsNullOrDefault() && !model.Id.IsNull() && !model.Id.IsDefaultGuid() && !reference.IsNullOrDefault() && !reference.ReferencedTo.IsNull() && reference.ProvidedFor == Provider && !reference.ProvidedFor.IsNullOrEmpty(); if (!result) { XException.InvalidArgs.Throw(); } // result = await AddReference( model: model, userInfo: userInfo, connectionId: connectionId, providedForId: reference.ReferencedTo ); // return result; } /// /// Remove Reference a Model for Specific Provided ID ... /// /// /// /// if null, removes all /// /// /// public async Task RemoveReference( XFileDto model, TKey providedForId, string connectionId = null, XUserClaimsInfoDto userInfo = null ) { // // Validate ... var result = !model.Id.IsNull() && !providedForId.IsNull() && !model.IsNullOrDefault() && !model.Id.IsDefaultGuid(); if (!result) { XException.InvalidArgs.Throw(); } // // Retrieve Model ... model = await FileProvider.Get(model.Id); if (model.IsNullOrDefault()) { XException.NotFound.Throw(); } // // Reading Referencing Indexes ... var indexes = await GetReferencingIndexes(providedForId); // // Check Model Reference to ID ... var referencesCount = await CountReferences(providedForId); result = referencesCount == 0; if (result) { // // There is not any Reference to Remove ... return result; } // // Check Model is Trully Referenced to ProvidedForId or not ... var reference = model.References .FirstOrDefault(r => r.ProvidedFor == Provider && r.ReferencedTo == $"{providedForId}"); result = !reference.IsNullOrDefault(); if (!result) { XException.ActionFailed.Throw(); } // // Remove Referenced Item from Model ... model.References = model.References .Where(r => !(r.ProvidedFor == Provider && r.ReferencedTo == $"{providedForId}")) .ToList(); // // Update DataBase ... model = await FileProvider.Update( id: model.Id, item: model, userInfo: userInfo, connectionId: connectionId ); // result = !model.IsNullOrDefault(); if (!result) { XException.ActionFailed.Throw(); } // // Get Max Exists Indexes ... var maxIndex = indexes.Max(); // // Here We Have to Re Arrange Indexed ... if (reference.Index < maxIndex) { // var startIndex = reference.Index + 1; for (int i = startIndex; i <= maxIndex; i++) { // var dto = await GetReference( forIndex: i, providedForId: providedForId ); if (!dto.IsNullOrDefault()) { // dto.References .ToList() .ForEach(iref => { // if ( iref.Index == i && iref.ProvidedFor == Provider && iref.ReferencedTo == reference.ReferencedTo ) { iref.Index = i - 1; } }); // dto = await FileProvider.Update( item: dto, id: dto.Id, userInfo: userInfo, connectionId: connectionId ); } } } // return result; } /// /// Remove Reference a Model for Specific XRefence ... /// /// /// /// /// /// public async Task RemoveReference( XFileDto model, XReference reference, string connectionId = null, XUserClaimsInfoDto userInfo = null ) { // if (model.IsNullOrDefault() || reference.IsNullOrDefault() || reference.ReferencedTo.IsNull() || reference.ProvidedFor != Provider) { XException.InvalidArgs.Throw(); } // var result = await RemoveReference( model: model, userInfo: userInfo, connectionId: connectionId, providedForId: reference.ReferencedTo ); // return result; } /// /// Remove All References to Specified Key ... /// /// /// /// /// public async Task RemoveReferences( TKey providedForId, string connectionId = null, XUserClaimsInfoDto userInfo = null ) { // // Validate ... if (providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // var result = false; var references = await GetAllReferences(providedForId); if (!references.IsNull() && references.HasChild()) { // foreach (var reference in references) { // // Remove Reference ... reference.References = reference.References .Where(r => r.ProvidedFor != Provider || (r.ProvidedFor == Provider && r.ReferencedTo != $"{providedForId}")) .ToList(); var updatedModel = await FileProvider.Update( item: reference, id: reference.Id, userInfo: userInfo, connectionId: connectionId ); if (!updatedModel.IsNullOrDefault()) { result = true; } } } // return result; } #endregion // #region Private ... private string GetProvidedID( TKey providedForId, char splitter = '_' ) { // // Validate ... if (providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // var result = $"{Provider}{splitter}{providedForId}"; return result; } private string GetIndexedProvidedID( TKey providedForId, int forIndex, char splitter = '_' ) { // var result = $"{GetProvidedID(providedForId, splitter)}{splitter}{forIndex}"; // return result; } private async Task> GetReferencingIndexes(TKey providedForId) { // // Validate ... var isValid = !providedForId.IsNull(); if (!isValid) { XException.InvalidArgs.Throw(); } // var result = new List(); var referencings = await GetAllReferencings(providedForId); isValid = !referencings.IsNull() && referencings.HasChild(); if (!isValid) { return result; } // result = referencings .OrderBy(r => r.Index) .Select(r => r.Index) .ToList(); // return result; } /// /// Check Specified Dto is Has Reference to Provider ... /// /// /// private async Task IsOwned(Guid id) { // // Validate ... var result = !id.IsNull() && !id.IsDefaultGuid(); if (!result) { XException.InvalidArgs.Throw(); } // // Check Exists ... result = await FileProvider.IsExists(id); if (!result) { XException.NotFound.Throw(); } // // Retrieve Dto ... var dto = await FileProvider.Get(id); result = !dto.IsNullOrDefault(); if (!result) { XException.ActionFailed.Throw(); } // result = !dto.References.IsNull() && dto.References.HasChild() && dto.References.Any(r => r.ProvidedFor == Provider); // return result; } /// /// Get Specified Owned Dto by File Name ... /// /// /// private async Task GetDto(string fileName) { // XFileDto result = null; // if (!fileName.IsNullOrEmpty()) { // try { // result = await FileProvider.FindOne(d => d.Name == fileName || d.Thumb == fileName || d.FileName == fileName || d.Path.Contains(fileName) || d.ThumbPath.Contains(fileName) ); if (!result.IsNullOrDefault()) { // // Check Owning ... var isOwned = await IsOwned(result.Id); if (!isOwned) { result = null; } } } catch { } } // return result; } #endregion } }