using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using xCategoryService.Interfaces; using xCategoryService.Interfaces.Dtos; using xCategoryService.Models.Dtos; using xCategoryService.Models.Entities; using xCategoryService.Providers.Dtos; using xCommons.Configurations; using xCommons.Extensions; using xExceptions.Constants; using xIdentityModels.Models; using xModels.Dtos; using xDataService.Extensions; using xPushService.Constants; using xPushService.Providers; using xDataService.Configuration; namespace xCategoryService.Providers { public class XCategoryProvider : XBaseProvided, IXCategoryProvider { private readonly IXCategoryServiceProvider provider; private readonly XAppConfiguration appConfiguration; private readonly XDataServiceConfiguration dataServiceConfiguration; private readonly IXCategoryTitleResourceProvider titleResourceProvider; private readonly IXCategoryDescriptionResourceProvider descriptionResourceProvider; public XCategoryProvider( XAppConfiguration appConfiguration, IXCategoryServiceProvider provider, XDataServiceConfiguration dataServiceConfiguration, IXCategoryTitleResourceProvider titleResourceProvider, IXCategoryDescriptionResourceProvider descriptionResourceProvider ) : base( provider, permittedRoles: new string[] { } ) { // this.provider = provider; this.appConfiguration = appConfiguration; this.titleResourceProvider = titleResourceProvider; this.dataServiceConfiguration = dataServiceConfiguration; this.descriptionResourceProvider = descriptionResourceProvider; } // #region Overrides ... public override bool IsOwned( XCategoryDto item, XUserClaimsInfoDto userInfo ) { return !item.IsNullOrDefault(); } public override bool IsOwned( XCategory item, XUserClaimsInfoDto userInfo ) { return !item.IsNullOrDefault(); } #endregion // #region Actions ... /// /// Check Has Specified Locale or not ... /// /// /// /// /// public async Task HasLocale( int id, string language, CancellationToken cancellationToken = default ) { // // Validate ... var isValid = id.IsValidIntId() && !language.IsNullOrEmpty(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Retrieve Resource ... var resource = await GetResource( id: id, cancellationToken: cancellationToken ); // // Check Result ... var result = !resource.IsNullOrDefault() && resource.Titles.Locales .Any(x => x.Language .Equals(language, System.StringComparison.InvariantCultureIgnoreCase)); // return result; } /// /// Load an Item by all of it's Related Translations ... /// /// /// /// public async Task GetResource( int id, CancellationToken cancellationToken = default ) { // var item = await Get( id: id, includeBuilder: null, cancellationToken: cancellationToken ); var titleResource = await titleResourceProvider.GetResource( identifier: id, cancellationToken: cancellationToken ); var descriptionResource = await descriptionResourceProvider.GetResource( identifier: id, cancellationToken: cancellationToken ); // var result = new XCategoryResourceDto { Item = item, Titles = titleResource, Descriptions = descriptionResource }; // return result; } /// /// Add an Item for Specified Language ... /// /// /// /// /// /// /// public async Task Add( XCategoryDto item, string language = null, string connectionId = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ) { // // Normalize ... language = !language.IsNullOrEmpty() ? language : appConfiguration.DefaultLanguage; // // Validate ... var isValid = !item.IsNullOrDefault(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Check Permission ... isValid = HasPermision(userInfo); if (!isValid) { XException.NotAllowed.Throw(); } // var id = -1; try { // var title = item.Title; var description = item.Description; // // Add Item Pure for ID Generating ... item = await base.Add( item: item, userInfo: userInfo, connectionId: null, // Since here We dont want notification ... cancellationToken: cancellationToken ); isValid = !item.IsNullOrDefault(); if (!isValid) { XException.ActionFailed.Throw(); } // id = item.Id; var titleResource = await titleResourceProvider .AddOrUpdateLocale( identifier: id, locale: new XLocaleResourceDto { Value = title, Language = language }, connectionId: connectionId, cancellationToken: cancellationToken ); var descriptionResource = await descriptionResourceProvider .AddOrUpdateLocale( identifier: id, locale: new XLocaleResourceDto { Value = description, Language = language }, connectionId: connectionId, cancellationToken: cancellationToken ); // // Update Category Dto ... item.Title = titleResource.Resource; item.Description = descriptionResource.Resource; item = await base.Update( id: id, item: item, userInfo: userInfo, connectionId: null, cancellationToken: cancellationToken ); } catch { XException.InvalidArgs.Throw(); } // // Validate Id ... isValid = id.IsValidIntId(); if (!isValid) { XException.ActionFailed.Throw(); } // // Retrieve Category Resourv=ce ... var result = await GetResource( id: id, cancellationToken: cancellationToken ); // // Sending Push Notification ... isValid = isValid && !result.IsNullOrDefault(); if (isValid) { // await provider.SendPush( connectionId: connectionId, cancellationToken: cancellationToken, payLoad: result.ToJSON(camelCase: true), action: XBaseEntityHubAction.Add.GetStringValue() ); } // return result; } /// /// Remove Item for Specified Language ... /// /// /// if null, Remove All /// /// /// /// public async Task Remove( int id, string language = null, string connectionId = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ) { // // Validate ... var isValid = id.IsValidIntId(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Check Item Exists ... isValid = await IsExists( id: id, cancellationToken: cancellationToken ); if (!isValid) { XException.NotFound.Throw(); } // // Validate Locale if Provided ... isValid = language.IsNullOrEmpty() || await HasLocale( id: id, language: language, cancellationToken: cancellationToken ); if (!isValid) { XException.NotFound.Throw(); } // var result = await GetResource( id: id, cancellationToken: cancellationToken ); if (language.IsNullOrEmpty()) { // // Remove All ... // // Titles ... await titleResourceProvider.RemoveLocales( identifier: id, connectionId: null, cancellationToken: cancellationToken ); // // Descriptions ... await descriptionResourceProvider.RemoveLocales( identifier: id, connectionId: null, cancellationToken: cancellationToken ); // // Remove Entity ... await provider.RemoveAsync( id: id, softDelete: false, saveChanges: true, connectionId: null, cancellationToken: cancellationToken ); // isValid = true; result.Titles.Locales = []; result.Descriptions.Locales = []; } else { // // Remove Only One Locale, and if there is one, Remove all ... // // Remove Title if Exists ... isValid = await titleResourceProvider.HasLocale( identifier: id, language: language, cancellationToken: cancellationToken ); if (isValid) { // await titleResourceProvider.RemoveLocale( identifier: id, language: language, connectionId: null, cancellationToken: cancellationToken ); } // // Remove Description if Exists ... isValid = await descriptionResourceProvider.HasLocale( identifier: id, language: language, cancellationToken: cancellationToken ); if (isValid) { // await descriptionResourceProvider.RemoveLocale( identifier: id, language: language, connectionId: null, cancellationToken: cancellationToken ); } // // Retrieve Resource ... result = await GetResource( id: id, cancellationToken: cancellationToken ); // // Check Has Locales ... isValid = result.Titles.Locales.HasChild(); if (!isValid) { // // Remove Item ... await provider.RemoveAsync( id: id, softDelete: false, saveChanges: true, connectionId: null, cancellationToken: cancellationToken ); // result.Titles.Locales = []; result.Descriptions.Locales = []; } // isValid = true; } // if (isValid) { // // Send Push ... await provider.SendPush( connectionId: connectionId, cancellationToken: cancellationToken, payLoad: result.ToJSON(camelCase: true), action: result.Titles.Locales.HasChild() ? "DeleteLocale" : XBaseEntityHubAction.Delete.GetStringValue() ); } // return result; } /// /// Update an Item for Specified Language ... /// /// /// /// /// /// /// /// public async Task Update( int id, XCategoryDto item, string language = null, string connectionId = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ) { // // Validate ... var isValid = id.IsValidIntId() && !item.IsNullOrDefault() && !language.IsNullOrEmpty() && !item.Title.IsNullOrEmpty(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Check Category Exists ... isValid = await IsExists( id: id, cancellationToken: cancellationToken ); if (!isValid) { XException.NotFound.Throw(); } // // Check Locale Exists ... isValid = await HasLocale( id: id, language: language, cancellationToken: cancellationToken ); if (!isValid) { XException.NotFound.Throw(); } // // Retrieve Category Dto for Validate Owining ... var dto = await Get( id: id, includeBuilder: null, cancellationToken: cancellationToken ); // // Check Permission ... isValid = !dto.IsNullOrDefault() && !userInfo.IsNullOrDefault() && (IsOwned( item: dto, userInfo: userInfo ) || HasPermision(userInfo)); if (!isValid) { XException.NotAllowed.Throw(); } // // Retrieve Exists Item Resource ... var resource = await GetResource( id: id, cancellationToken: cancellationToken ); // var titleUpdated = false; var descriptionUpdated = false; // // Update Title ... resource.Titles.Locales .ToList() .ForEach(tl => { // if (tl.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase)) { // titleUpdated = true; tl.Value = item.Title; } }); // // Update Description ... resource.Descriptions.Locales .ToList() .ForEach(dl => { // if (dl.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase)) { // descriptionUpdated = true; dl.Value = item.Description; } }); // // Validate Title and Description Updated ... isValid = titleUpdated && descriptionUpdated; if (!isValid) { XException.ActionFailed.Throw(); } // resource = await Refresh( resource: resource, userInfo: userInfo, connectionId: connectionId, cancellationToken: cancellationToken ); // return resource; } /// /// Refresh a Resource ... /// /// /// /// /// /// public async Task Refresh( XCategoryResourceDto resource, string connectionId = null, XUserClaimsInfoDto userInfo = null, CancellationToken cancellationToken = default ) { // // Validate Args ... var isValid = !resource.IsNull(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Check Permission ... isValid = HasPermision(userInfo); if (!isValid) { XException.NotAllowed.Throw(); } // var id = -1; var isDelete = false; XCategoryResourceDto result = null; // // Check Item Exists ... XCategoryDto dto = (resource.Item.IsNullOrDefault() || !resource.Item.Id.IsValidIntId()) ? null : await Get( id: resource.Item.Id, includeBuilder: null, cancellationToken: cancellationToken ); var isExists = !dto.IsNullOrDefault(); // // Do Referesh Based on Existance ... var isNew = !isExists && (resource.Item.IsNullOrDefault() || !resource.Item.Id.IsValidIntId()); if (isNew) { // // Add ... // In this Senario, for Add a Category by Providing Resource // the item must clean, either Title and Description, and tge Locales Provided by Resource // ust Contains Values ... // the Add Starts by Titles and only Items Add Which has Titles, since Title is Mandatory // and Description is Optional ... isValid = // // Resource ... (resource.Item.IsNullOrDefault() || (!resource.Item.Id.IsValidIntId() && resource.Item.Title.IsNullOrEmpty() && resource.Item.Description.IsNullOrEmpty())) && // // Titles ... !resource.Titles.IsNullOrDefault() && resource.Titles.Locales.HasChild(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Add Temp Category ... var unique = Guid.NewGuid(); var temp = new XCategoryDto { Title = $"TmpT_{unique}", Description = $"TmpD_{unique}", }; temp = await provider.AddAsync( item: temp, saveChanges: true, connectionId: null, cancellationToken: cancellationToken ); isValid = !temp.IsNullOrDefault() && temp.Id.IsValidIntId(); if (!isValid) { XException.ActionFailed.Throw(); } id = temp.Id; // // Update Title and Description ... var titleResource = titleResourceProvider.GetResourceTitle(id); var descriptionResource = descriptionResourceProvider.GetResourceTitle(id); temp.Title = titleResource; temp.Description = descriptionResource; temp = await provider.UpdateAsync( id: id, item: temp, saveChanges: true, connectionId: null, cancellationToken: cancellationToken ); // // Loop through Titles ... foreach (var locale in resource.Titles.Locales) { // // Add Or Update Locale ... await titleResourceProvider.AddOrUpdateLocale( locale: locale, identifier: id, connectionId: null, cancellationToken: cancellationToken ); var desLocale = resource.Descriptions.Locales.FirstOrDefault( dl => dl.Language == locale.Language); if (!desLocale.IsNullOrDefault()) { // // Add Descriptions ... await descriptionResourceProvider.AddOrUpdateLocale( identifier: id, locale: desLocale, connectionId: null, cancellationToken: cancellationToken ); } } } else if (isExists) { // // Re State ... id = resource.Item.Id; // // Try to Refresh Resource State ... // // Check Delete ... isDelete = !resource.Titles.Locales.HasChild(); if (isDelete) { // // Handle Delete ... await Remove( id: id, language: null, userInfo: userInfo, connectionId: null, cancellationToken: cancellationToken ); } else { // // Handle Update ... // #region Remove ... // // Titles ... var removeTitles = result.Titles.Locales.Where( l => !resource.Titles.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase)) ); foreach (var locale in removeTitles) { // await titleResourceProvider.RemoveLocale( identifier: id, connectionId: null, language: locale.Language, cancellationToken: cancellationToken ); } // // Descriptions ... var removeDescriptions = result.Descriptions.Locales.Where( l => !resource.Descriptions.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase)) ); foreach (var locale in removeDescriptions) { // await descriptionResourceProvider.RemoveLocale( identifier: id, connectionId: null, language: locale.Language, cancellationToken: cancellationToken ); } #endregion // #region Update ... // // Titles ... var updateTitles = result.Titles.Locales.Where( l => resource.Titles.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase)) ); foreach (var locale in updateTitles) { // await titleResourceProvider.AddOrUpdateLocale( identifier: id, locale: locale, connectionId: null, cancellationToken: cancellationToken ); } // // Descriptions ... var updateDescriptions = result.Descriptions.Locales.Where( l => resource.Descriptions.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase)) ); foreach (var locale in updateDescriptions) { // await descriptionResourceProvider.AddOrUpdateLocale( identifier: id, locale: locale, connectionId: null, cancellationToken: cancellationToken ); } #endregion // #region New ... // // Titles ... var newTitles = resource.Titles.Locales.Where( l => !result.Titles.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase)) ); foreach (var locale in newTitles) { // await titleResourceProvider.AddOrUpdateLocale( identifier: id, locale: locale, connectionId: null, cancellationToken: cancellationToken ); } // // Descriptions ... var newDescriptions = resource.Descriptions.Locales.Where( l => !result.Descriptions.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase)) ); foreach (var locale in newDescriptions) { // await descriptionResourceProvider.AddOrUpdateLocale( identifier: id, locale: locale, connectionId: null, cancellationToken: cancellationToken ); } #endregion } } // // Validate Action ... isValid = id.IsValidIntId(); if (!isValid) { XException.ActionFailed.Throw(); } // // Get Result ... result = isDelete ? resource : await GetResource( id: id, cancellationToken: cancellationToken ); isValid = !result.IsNullOrDefault(); // // Send Notification ... if (isValid) { // await provider.SendPush( connectionId: connectionId, cancellationToken: cancellationToken, payLoad: result.ToJSON(camelCase: true), action: isNew ? XBaseEntityHubAction.Add.GetStringValue() : isDelete ? XBaseEntityHubAction.Delete.GetStringValue() : XBaseEntityHubAction.Update.GetStringValue() ); } // return result; } /// /// Get All Resources as Async Enumerable ... /// /// /// public async IAsyncEnumerable GetAllResourcesAsAsyncEnumerable( [EnumeratorCancellation] CancellationToken cancellationToken = default ) { // var enumerable = provider.GetAllAsAsyncEnumerable( includeBuilder: null, ignoreSoftDeleteds: true, cancellationToken: cancellationToken, orderBuilder: x => x.OrderBy(y => y.Id) ); // await foreach (var dto in enumerable) { // if (cancellationToken.IsCancellationRequested) { yield break; } // var resource = await GetResource( id: dto.Id, cancellationToken: cancellationToken ); yield return resource; } } /// /// Get All Resources ... /// /// /// public async Task> GetAllResources( CancellationToken cancellationToken = default ) { // var result = new List(); // var enumerable = GetAllResourcesAsAsyncEnumerable(cancellationToken); await foreach (var item in enumerable) { // if (cancellationToken.IsCancellationRequested) { break; } // result.Add(item); } // return result; } /// /// Find One Resource ... /// /// /// /// public async Task FindOneResource( Expression> predicate = null, CancellationToken cancellationToken = default ) { // var isApproved = false; XCategoryResourceDto result = null; var enumerable = GetAllResourcesAsAsyncEnumerable(cancellationToken); await foreach (var item in enumerable) { // // Check Cancellation Token ... if (cancellationToken.IsCancellationRequested) { break; } // // Detect Predicate Condition ... isApproved = predicate.Compile()(item); if (isApproved) { // result = item; break; } } // return result; } /// /// Find Many Resource ... /// /// /// /// public async Task> FindManyResource( Expression> predicate = null, Func, IOrderedQueryable> orderBuilder = null, CancellationToken cancellationToken = default ) { // var isApproved = false; var result = new List(); var enumerable = GetAllResourcesAsAsyncEnumerable(cancellationToken); await foreach (var item in enumerable) { // // Check Cancellation Token ... if (cancellationToken.IsCancellationRequested) { break; } // // Detect Predicate Condition ... isApproved = predicate.Compile()(item); if (isApproved) { result.Add(item); } } // // Apply Order ... if (!orderBuilder.IsNull()) { // result = orderBuilder(result.AsQueryable()) .ToList(); } // return result; } /// /// Retrieve Resources by Query Pattern ... /// /// /// /// /// /// public async Task> QueryResources( XQuery query = null, Expression> predicate = null, Func, IOrderedQueryable> orderBuilder = null, CancellationToken cancellationToken = default ) { // // Normalize Query ... query = query.NormalizeQuery(dataServiceConfiguration); // var items = await FindManyResource( predicate: predicate, orderBuilder: orderBuilder, cancellationToken: cancellationToken ); 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], PageSize = query.PageSize, TotalPages = totalPagesCount, TotalItems = totalItemsCount, TotalFilteredPages = filteredPagesCount, TotalFilteredItems = filteredItemsCount }; // return result; } #endregion } }