From 2f5cf69057005580f874f010cb175ba34fe14eea Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Fri, 7 Aug 2026 01:24:40 +0330 Subject: [PATCH] add Context Generator Script and last changes ... --- ContextGenerator.ps1 | 9 + Documents/GeneratedContext/14050516.txt | 101580 +++++++++++++++++++++ Modules/xDataService | 2 +- 3 files changed, 101590 insertions(+), 1 deletion(-) create mode 100644 ContextGenerator.ps1 create mode 100644 Documents/GeneratedContext/14050516.txt diff --git a/ContextGenerator.ps1 b/ContextGenerator.ps1 new file mode 100644 index 0000000..379a16c --- /dev/null +++ b/ContextGenerator.ps1 @@ -0,0 +1,9 @@ +Get-ChildItem -Recurse -File -Include Dockerfile,docker-compose.yaml,*.cs | +Where-Object { + $_.DirectoryName -notmatch '\\(bin|obj)(\\|$)' +} | +ForEach-Object { + "`n### FILE: $($_.FullName)`n" + Get-Content $_.FullName +} | +Out-File .\all-code.txt -Encoding utf8 \ No newline at end of file diff --git a/Documents/GeneratedContext/14050516.txt b/Documents/GeneratedContext/14050516.txt new file mode 100644 index 0000000..d3aa070 --- /dev/null +++ b/Documents/GeneratedContext/14050516.txt @@ -0,0 +1,101580 @@ + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Configurations\Entities\XAiConversationEntityConfiguration.cs + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xAiModels.Constants; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Configurations.Entities +{ + public class XAiConversationEntityConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable(XAiModelsConstants.XAiConversationTable); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Configurations\Entities\XAiConversationMappingAction.cs + +using System; +using AutoMapper; +using xAiModels.Interfaces; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xIdentityService.Extensions; +using xIdentityService.Interfaces; + +namespace xAiModels.Configurations.Entities +{ + public class XAiConversationMappingAction : IMappingAction + { + private readonly IXIdentityProvider identityProvider; + private readonly IXAiProjectTitleResourceProvider projectTitleResourceProvider; + private readonly IXAiConversationTitleResourceProvider conversationTitleResourceProvider; + private readonly IXAiProjectDescriptionResourceProvider projectDescriptionResourceProvider; + + public XAiConversationMappingAction( + IXIdentityProvider identityProvider, + IXAiProjectTitleResourceProvider projectTitleResourceProvider, + IXAiConversationTitleResourceProvider conversationTitleResourceProvider, + IXAiProjectDescriptionResourceProvider projectDescriptionResourceProvider + ) + { + this.identityProvider = identityProvider; + this.projectTitleResourceProvider = projectTitleResourceProvider; + this.conversationTitleResourceProvider = conversationTitleResourceProvider; + this.projectDescriptionResourceProvider = projectDescriptionResourceProvider; + } + + public void Process( + XAiConversation source, + XAiConversationDto destination, + ResolutionContext context + ) + { + // + // Retrieve Owner ... + var device = identityProvider.GetDevice(); + var ownerUserInfo = identityProvider.GetUserInfo( + device: device, + userSelectByParam: source.OwnerId + ) + .GetAwaiter() + .GetResult(); + var owner = ownerUserInfo.ToXPersonDto(); + + // + // Setting Owner ... + destination.Owner = owner; + + // Retrieve Project Info ... + throw new NotImplementedException(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Configurations\Entities\XAiMessageEntityConfiguration.cs + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xAiModels.Constants; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Configurations.Entities +{ + public class XAiMessageEntityConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable(XAiModelsConstants.XAiMessageTable); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Configurations\Entities\XAiMessageMappingAction.cs + +using System; +using AutoMapper; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xIdentityService.Extensions; +using xIdentityService.Interfaces; + +namespace xAiModels.Configurations.Entities +{ + public class XAiMessageMappingAction : IMappingAction + { + private readonly IXIdentityProvider identityProvider; + + public XAiMessageMappingAction( + IXIdentityProvider identityProvider + ) + { + this.identityProvider = identityProvider; + } + + public void Process( + XAiMessage source, + XAiMessageDto destination, + ResolutionContext context + ) + { + // + // Retrieve Owner ... + var device = identityProvider.GetDevice(); + var ownerUserInfo = identityProvider.GetUserInfo( + device: device, + userSelectByParam: source.OwnerId + ) + .GetAwaiter() + .GetResult(); + var owner = ownerUserInfo.ToXPersonDto(); + + // + // Setting Owner ... + destination.Owner = owner; + + // + // Retrieve Owner ... + // Retrieve Conversation ... + throw new NotImplementedException(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Configurations\Entities\XAiProjectEntityConfiguration.cs + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xAiModels.Constants; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Configurations.Entities +{ + public class XAiProjectEntityConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable(XAiModelsConstants.XAiProjectTable); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Configurations\Entities\XAiProjectMappingAction.cs + +using System; +using AutoMapper; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xIdentityService.Extensions; +using xIdentityService.Interfaces; + +namespace xAiModels.Configurations.Entities +{ + public class XAiProjectMappingAction : IMappingAction + { + private readonly IXIdentityProvider identityProvider; + + public XAiProjectMappingAction( + IXIdentityProvider identityProvider + ) + { + this.identityProvider = identityProvider; + } + + public void Process( + XAiProject source, + XAiProjectDto destination, + ResolutionContext context + ) + { + // + // Retrieve Owner ... + var device = identityProvider.GetDevice(); + var ownerUserInfo = identityProvider.GetUserInfo( + device: device, + userSelectByParam: source.OwnerId + ) + .GetAwaiter() + .GetResult(); + var owner = ownerUserInfo.ToXPersonDto(); + + // + // Setting Owner ... + destination.Owner = owner; + + // + // Retrieve Owner ... + // Retrieve Title Resources ... + // Retrieve Description Resources ... + throw new NotImplementedException(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Configurations\XAiApiConfiguration.cs + +using xAiModels.Constants; +using xAiModels.Models; + +namespace xAiModels.Configurations +{ + public class XAiApiConfiguration + { + /// + /// Model Providers ... + /// + public XAiModelDescriptor[] Models { get; set; } = []; + + /// + /// Each User must Has a Default Project, this is Title Resource Identifier ... + /// + public string DefaultProjectTitleResource { get; set; } = XAiModelsConstants.DefaultXAiProjectTitleResourceID; + + /// + /// Each User must Has a Default Project, this is Description Resource Identifier ... + /// + public string DefaultProjectDescriptionResource { get; set; } = XAiModelsConstants.DefaultXAiProjectDescriptionResourceID; + + /// + /// Each User must Has a Default Conversation, this is Title Resource Identifier ... ... + /// + public string DefaultConversationTitleResource { get; set; } = XAiModelsConstants.DefaultXAiConversationTitleResourceID; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Configurations\XAiModelMappersProfile.cs + +using AutoMapper; +using xAiModels.Configurations.Entities; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; + +namespace xAiModels.Configurations +{ + public class XAiModelMappersProfile : Profile + { + public XAiModelMappersProfile() + { + // + // XAiMessage ... + CreateMap() + .AfterMap(); + + // + // XAiProject ... + CreateMap() + .AfterMap(); + + // + // XAiConversation ... + CreateMap() + .AfterMap(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Constants\ConfigurationNodeNames.cs + +namespace xAiModels.Constants +{ + public partial struct ConfigurationNodeNames + { + public const string AI_API_NODE = "AiApiConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Constants\XAiChatRole.cs + +using xExceptions.Attributes; + +namespace xAiModels.Constants +{ + public struct XAiChatRoles + { + public const string None = "none"; + public const string User = "user"; + public const string Tool = "tool"; + public const string System = "system"; + public const string Assistant = "assistant"; + } + + public enum XAiChatRole + { + [StringValue(XAiChatRoles.None)] + None, + [StringValue(XAiChatRoles.User)] + User, + [StringValue(XAiChatRoles.Tool)] + Tool, + [StringValue(XAiChatRoles.System)] + System, + [StringValue(XAiChatRoles.Assistant)] + Assistant, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Constants\XAiModelProviderType.cs + +namespace xAiModels.Constants +{ + /// + /// Specifiy Provider Type ... + /// + public enum XAiModelProviderType + { + None, + Ollama, + DeepSeek, + HuggingFace + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Constants\XAiModelsConstants.cs + +namespace xAiModels.Constants +{ + public struct XAiModelsConstants + { + // + // Service Extensions Log Tag ... + public const string XAiModelsDILogTag = "XAiModels"; + + // + // Table Mapping Identifiers ... + public const string XAiMessageTable = "AiMessages"; + public const string XAiProjectTable = "AiProjects"; + public const string XAiConversationTable = "AiConversations"; + + // + // GraphQL Collection Mappings ... + public const string XAiMessageSingleName = "aiMessage"; + public const string XAiProjectSingleName = "aiProject"; + public const string XAiConversationSignleName = "aiConversation"; + + public const string XAiMessageCollectionName = "aiMessages"; + public const string XAiProjectCollectionName = "AiProjectsa"; + public const string XAiConversationCollectionName = "aiConversations"; + + // + // Hubs ... + public const string XAiMessageDtoHub = "aiMessageDto"; + public const string XAiMessageEntityHub = "aiMessageEntity"; + + public const string XAiProjectDtoHub = "aiProjectDto"; + public const string XAiProjectEntityHub = "aiProjectEntity"; + + public const string XAiConversationDtoHub = "aiConversationDto"; + public const string XAiConversationEntityHub = "aiConversationEntity"; + + // + // Resource Provider Identifiers ... + public const string XAiProjectTitleIdentifier = "AiPrjT"; + public const string XAiProjectDescriptionIdentifier = "AiPrjD"; + public const string XAiConversationTitleIdentifier = "AiCnvrT"; + + // + // EF Repositories Helper Extensions ... + public const string GetXAiMessageEFRepositoryDescriptor = "GetXAiMessageEFRepositoryDescriptor"; + public const string GetXAiProjectEFRepositoryDescriptor = "GetXAiProjectEFRepositoryDescriptor"; + public const string GetXAiConversationEFRepositoryDescriptor = "GetXAiConversationEFRepositoryDescriptor"; + + // + public const string DefaultXAiProjectTitleResourceID = "XAiPrjT"; + public const string DefaultXAiConversationTitleResourceID = "XAiCnvT"; + public const string DefaultXAiProjectDescriptionResourceID = "XAiPrjD"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\DI\XDIHelperExtension.cs + +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using xAiModels.Providers.Dtos; +using xAiModels.Providers.Entities; +using xCommons.Extensions; +using xDataService.Constants; +using xDataService.Db; +using xDataService.Models; +using xExceptions.Constants; +using xPushService.Helpers; +using xPushService.DI; +using xDataService.DI; +using xAiModels.Interfaces.Dtos; +using xAiModels.Helpers; +using xCommons.Helpers; +using xAiModels.Interfaces.Entities; +using xAiModels.Interfaces; +using xAiModels.Providers; +using xAiModels.Constants; + +namespace xAiModels.DI +{ + /// + /// Service Registration Extensions Classes ... + /// + public static class XDIHelperExtension + { + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXAiDataService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + AddAiDataService( + contextType: null, + services: services, + lifeTime: lifeTime, + repositoryType: repositoryType + ); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + public static void AddXAiDataService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + where TContext : XDbContext + { + AddAiDataService( + services: services, + lifeTime: lifeTime, + contextType: typeof(TContext), + repositoryType: repositoryType + ); + } + + /// + /// Use XSubCategoryService Middleware ... + /// + /// + /// + public static void UseXAiDataService( + this IApplicationBuilder app, + bool isDevelopmentEnvironment = false + ) + { + // + #region Using Hubs ... + // + var pushHelper = new XPushServiceHelper(); + + // + // XAiMessage ... + pushHelper.AddHub(XAiModelsConstants.XAiMessageDtoHub); + pushHelper.AddHub(XAiModelsConstants.XAiMessageEntityHub); + + // + // XAiProject ... + pushHelper.AddHub(XAiModelsConstants.XAiProjectDtoHub); + pushHelper.AddHub(XAiModelsConstants.XAiProjectEntityHub); + + // + // XAiConversation ... + pushHelper.AddHub(XAiModelsConstants.XAiConversationDtoHub); + pushHelper.AddHub(XAiModelsConstants.XAiConversationEntityHub); + + // + app.UseXPushService(pushHelper); + #endregion + + // + // Using XAiConversation Repository Descriptor ... + if (!aiConversationDescriptor.IsNull()) + { + // + app.UseXRepository( + descriptor: aiConversationDescriptor, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + + // + // Using XAiMessage Repository Descriptor ... + if (!aiMessageDescriptor.IsNull()) + { + // + app.UseXRepository( + descriptor: aiMessageDescriptor, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + + // + // Using XAiProject Repository Descriptor ... + if (!aiProjectDescriptor.IsNull()) + { + // + app.UseXRepository( + descriptor: aiProjectDescriptor, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + + // + // Create an Scope for Services Acces ... + // Handle Seeding Category/SubCategory Resources ... + // TODO: Enable Seeding if Required ... + // using (var scope = app.ApplicationServices.CreateScope()) + // { + // // + // // Retrieve Configuratuions ... + // var aiMessageProvider = scope.ServiceProvider.GetService(); + // var aiProjectProvider = scope.ServiceProvider.GetService(); + // var aiConversationProvider = scope.ServiceProvider.GetService(); + // var configuration = scope.ServiceProvider.GetService(); + // if (!configuration.IsNullOrDefault() && + // !categoryProvider.IsNullOrDefault() && + // !subaiConversationDescriptor.IsNullOrDefault() + // ) + // { + // // + // // Logging ... + // Log("Start Seeding ..."); + + // // + // // Count Data in Category ... + // var hasCategory = (categoryProvider.Count().GetAwaiter().GetResult()) > 0; + // var hasSubCategory = (subCategoryProvider.Count().GetAwaiter().GetResult()) > 0; + + // // + // // Seed Category Resources ... + // var canSeed = + // !hasCategory && + // configuration.Categories.HasChild(); + // if (canSeed) + // { + // // + // // Loop through Provided items ... + // foreach (var cat in configuration.Categories) + // { + // // + // try + // { + // // + // Log($"Start Seeding Category: {cat.Titles.Locales.ToArray()[0].Value} ..."); + + // // + // categoryProvider.Refresh( + // resource: cat, + // userInfo: null, + // connectionId: null, + // cancellationToken: default + // ) + // .GetAwaiter() + // .GetResult(); + + // // + // Log($"Seeding Category: {cat.Titles.Locales.ToArray()[0].Value} Finished Successfully ..."); + // } + // catch (Exception ex) + // { + // Log($"Seeding Category: {cat.Titles.Locales.ToArray()[0].Value} Failed due: {ex.Message} ..."); + // } + // } + // } + + // // + // // Seed SubCategory Resources ... + // canSeed = + // !hasSubCategory && + // configuration.SubCategories.HasChild(); + // if (canSeed) + // { + // // + // // Loop through Provided items ... + // foreach (var subcat in configuration.SubCategories) + // { + // // + // try + // { + // // + // Log($"Start Seeding SubCategory: {subcat.Titles.Locales.ToArray()[0].Value} ..."); + + // // + // subCategoryProvider.Refresh( + // userInfo: null, + // resource: subcat, + // connectionId: null, + // cancellationToken: default + // ) + // .GetAwaiter() + // .GetResult(); + + // // + // Log($"Seeding SubCategory: {subcat.Titles.Locales.ToArray()[0].Value} Finished Successfully ..."); + // } + // catch (Exception ex) + // { + // Log($"Seeding SubCategory: {subcat.Titles.Locales.ToArray()[0].Value} Failed due: {ex.Message} ..."); + // } + // } + // } + + // // + // Log("Finish Seeding ..."); + // } + // } + } + + // + #region Private ... + private static XRepositoryDescriptor aiMessageDescriptor = null; + private static XRepositoryDescriptor aiProjectDescriptor = null; + private static XRepositoryDescriptor aiConversationDescriptor = null; + + /// + /// a LogTag for Service ... + /// + private static string XLogTag = XAiModelsConstants.XAiModelsDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + private static void AddAiDataService( + 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: + // + // XAiMessage ... + aiMessageDescriptor = typeof(XAiModelsHelper) + .InvokeGenericMethod( + args: null, + runtimeType: contextType, + methodName: XAiModelsConstants.GetXAiMessageEFRepositoryDescriptor + ); + + // + // XAiProject ... + aiProjectDescriptor = typeof(XAiModelsHelper) + .InvokeGenericMethod( + args: null, + runtimeType: contextType, + methodName: XAiModelsConstants.GetXAiProjectEFRepositoryDescriptor + ); + + // + // XAiConversation ... + aiConversationDescriptor = typeof(XAiModelsHelper) + .InvokeGenericMethod( + args: null, + runtimeType: contextType, + methodName: XAiModelsConstants.GetXAiConversationEFRepositoryDescriptor + ); + break; + + // + case XRepositoryType.Mongo: + // + // XAiMessage ... + aiMessageDescriptor = XAiModelsHelper.GetXAiMessageMongoRepositoryDescriptor(); + + // + // XAiProject ... + aiProjectDescriptor = XAiModelsHelper.GetXAiProjectMongoRepositoryDescriptor(); + + // + // XAiConversation ... + aiConversationDescriptor = XAiModelsHelper.GetXAiConversationMongoRepositoryDescriptor(); + break; + + // + case XRepositoryType.InMemory: + // + // XAiMessage ... + aiMessageDescriptor = XAiModelsHelper.GetXAiMessageInMemoryRepositoryDescriptor(); + + // + // XAiProject ... + aiProjectDescriptor = XAiModelsHelper.GetXAiProjectInMemoryRepositoryDescriptor(); + + // + // XAiConversation ... + aiConversationDescriptor = XAiModelsHelper.GetXAiConversationInMemoryRepositoryDescriptor(); + break; + } + + // + #region Register Repository Descriptor ... + // + // XAiMessage ... + if (!aiMessageDescriptor.IsNull()) + { + // + services.AddXRepository( + lifeTime: lifeTime, + descriptor: aiMessageDescriptor + ); + } + + // + // XAiProject ... + if (!aiProjectDescriptor.IsNull()) + { + // + services.AddXRepository( + lifeTime: lifeTime, + descriptor: aiProjectDescriptor + ); + } + + // + // XAiConversation ... + if (!aiConversationDescriptor.IsNull()) + { + // + services.AddXRepository( + lifeTime: lifeTime, + descriptor: aiConversationDescriptor + ); + } + #endregion + + // + #region Register Services ... + // + #region AiMessage ... + // + // Models ... + services.Add( + new ServiceDescriptor(typeof(IXAiMessageServiceProvider), typeof(XAiMessageServiceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXAiMessageRepositoryService), typeof(XAiMessageRepositoryService), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXAiMessageRepositoryProvider), typeof(XAiMessageRepositoryProvider), lifeTime) + ); + #endregion + + // + #region AiProject ... + // + // Models ... + services.Add( + new ServiceDescriptor(typeof(IXAiProjectServiceProvider), typeof(XAiProjectServiceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXAiProjectRepositoryService), typeof(XAiProjectRepositoryService), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXAiProjectRepositoryProvider), typeof(XAiProjectRepositoryProvider), lifeTime) + ); + + // + // Resource Providers ... + services.Add( + new ServiceDescriptor(typeof(IXAiProjectTitleResourceProvider), typeof(XAiProjectTitleResourceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXAiProjectDescriptionResourceProvider), typeof(XAiProjectDescriptionResourceProvider), lifeTime) + ); + #endregion + + // + #region AiConversation ... + // + // Models ... + services.Add( + new ServiceDescriptor(typeof(IXAiConversationServiceProvider), typeof(XAiConversationServiceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXAiConversationRepositoryService), typeof(XAiConversationRepositoryService), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXAiConversationRepositoryProvider), typeof(XAiConversationRepositoryProvider), lifeTime) + ); + + // + // Resource Providers ... + services.Add( + new ServiceDescriptor(typeof(IXAiConversationTitleResourceProvider), typeof(XAiConversationTitleResourceProvider), lifeTime) + ); + #endregion + #endregion + + // + Log("Service Regitration Succeed ..."); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Extensions\ChatResponseExtensions.cs + +using Microsoft.Extensions.AI; +using xCommons.Extensions; + +namespace xAiModels.Extensions +{ + public static class ChatResponseExtensions + { + /// + /// Validate a Chat Response ... + /// + /// + /// + public static bool IsValid(this ChatResponse source) + { + // + var result = + source is not null && + !source.IsNullOrDefault() && + !string.IsNullOrWhiteSpace(source.Text) && + source.FinishReason == ChatFinishReason.Stop; + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Extensions\ChatRoleExtensions.cs + +using Microsoft.Extensions.AI; +using xAiModels.Constants; +using xCommons.Extensions; + +namespace xAiModels.Extensions +{ + /// + /// Extensions for ChatRole ... + /// + public static class ChatRoleExtensions + { + /// + /// Normalize an String to Propper Chat Role Value ... + /// > + /// + /// + /// + public static string GetChatRoleTitle(this string source) + { + // + var result = XAiChatRoles.None; + + // + if (source.ToNormalString() == XAiChatRoles.None.ToNormalString()) + { + result = XAiChatRoles.None; + } + else if (source.ToNormalString() == XAiChatRoles.User.ToNormalString()) + { + result = XAiChatRoles.User; + } + else if (source.ToNormalString() == XAiChatRoles.Tool.ToNormalString()) + { + result = XAiChatRoles.Tool; + } + else if (source.ToNormalString() == XAiChatRoles.System.ToNormalString()) + { + result = XAiChatRoles.System; + } + else if (source.ToNormalString() == XAiChatRoles.Assistant.ToNormalString()) + { + result = XAiChatRoles.Assistant; + } + + // + return result; + } + + /// + /// Get Chat Role Title from Chat Role Class ... + /// + /// + /// + /// + public static string GetRole(this ChatRole source) + { + // + var result = source.Value + .GetChatRoleTitle(); + + // + return result; + } + + /// + /// Generate Chat Role Class Based on role Title ... + /// + /// + /// + public static ChatRole ToChatRole(this string source) + { + // + var role = source.GetChatRoleTitle(); + ChatRole result = new ChatRole(role); + + // + return result; + } + + /// + /// Generate Chat Role Class Based on role Title ... + /// + /// + /// + public static ChatRole ToChatRole(this XAiChatRole source) + { + // + var role = source.GetStringValue(); + ChatRole result = new ChatRole(role); + + // + return result; + } + + /// + /// Extract Chat Role Enum from Chat Role ... + /// + /// + /// + /// + /// + public static XAiChatRole ToChatRole(this ChatRole source) + { + // + var role = source + .GetRole() + .GetChatRoleTitle(); + + // + XAiChatRole result = XAiChatRole.User; + + // + if (role == XAiChatRoles.User) + { + result = XAiChatRole.User; + } + else if (role == XAiChatRoles.Tool) + { + result = XAiChatRole.Tool; + } + else if (role == XAiChatRoles.System) + { + result = XAiChatRole.System; + } + else if (role == XAiChatRoles.Assistant) + { + result = XAiChatRole.Assistant; + } + + // + return result; + } + + /// + /// Validate an String as Chat Role ... + /// + /// + /// + public static bool IsValid(this string source) + { + // + var result = + source != XAiChatRoles.None && + source.ToNormalString() != XAiChatRoles.None.ToNormalString(); + + // + return result; + } + + /// + /// Validate Role ... + /// > + /// + /// + /// + public static bool IsValid(this XAiChatRole source) + { + // + var result = source != XAiChatRole.None; + return result; + } + + /// + /// Validate Role ... + /// > + /// + /// + /// + public static bool IsValid(this ChatRole source) + { + // + var role = source.ToChatRole(); + var result = role.IsValid(); + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Extensions\XAiApiConfigurationExtensions.cs + +using System.Linq; +using xAiModels.Configurations; +using xAiModels.Models; +using xCommons.Extensions; + +namespace xAiModels.Extensions +{ + public static class XAiApiConfigurationExtensions + { + /// + /// Validate Api Configuration ... + /// + /// + /// + public static bool IsValid(this XAiApiConfiguration source) + { + return + !source.IsNullOrDefault() && + source.Models.HasChild(); + } + + /// + /// Retrieve Default Model Descriptor ... + /// + /// + /// + public static XAiModelDescriptor GetDefaultModel(this XAiApiConfiguration source) + { + // + XAiModelDescriptor result = null; + + // + if (source.IsValid()) + { + result = source.Models.First(); + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Helpers\XAiModelsHelper.cs + +using System; +using GraphQL.Types; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xAiModels.Providers.Entities; +using xAiModels.Providers.GraphQL; +using xDataService.Db; +using xDataService.Models; +using xDataService.Providers; + +namespace xAiModels.Helpers +{ + public static class XAiModelsHelper + { + // + #region XAiConversation ... + // + #region EF Repository Descriptor Provider(s) ... + public static XEFRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationEFRepository, + TContext + > GetXAiConversationEFRepositoryDescriptor() + where TContext : XDbContext + { + // + var result = new XEFRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationEFRepository, + TContext + >(); + + // + return result; + } + + public static XEFRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationEFRepository, + TContext, + IXAiConversationSeeder, + TSeederImplementation + > GetXAiConversationEFRepositoryDescriptor() + where TContext : XDbContext + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XEFRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationEFRepository, + TContext, + IXAiConversationSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region Mongo Repository Descriptor Provider(s) ... + public static XMongoRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationMongoRepository + > GetXAiConversationMongoRepositoryDescriptor() + { + // + var result = new XMongoRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationMongoRepository + >(); + + // + return result; + } + + public static XMongoRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationMongoRepository, + IXAiConversationSeeder, + TSeederImplementation + > GetXAiConversationMongoRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XMongoRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationMongoRepository, + IXAiConversationSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region InMemory Repository Descriptor Provider(s) ... + public static XInMemoryRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationInMemoryRepository + > GetXAiConversationInMemoryRepositoryDescriptor() + { + // + var result = new XInMemoryRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationInMemoryRepository + >(); + + // + return result; + } + + public static XInMemoryRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationInMemoryRepository, + IXAiConversationSeeder, + TSeederImplementation + > GetXAiConversationInMemoryRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XInMemoryRepositoryDescriptor< + XAiConversation, + Guid, + IXAiConversationKeyGenerator, + XAiConversationKeyGenerator, + IXAiConversationRepositoryEvents, + XAiConversationRepositoryEvents, + XAiConversationGraphType, + GuidGraphType, + XAiConversationGraphQuery, + IXAiConversationGraphQLTypeHelper, + XAiConversationGraphQLTypeHelper, + XAiConversationGraphSchema, + IXAiConversationRepository, + XAiConversationInMemoryRepository, + IXAiConversationSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + #endregion + + // + #region XAiMessage ... + // + #region EF Repository Descriptor Provider(s) ... + public static XEFRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageEFRepository, + TContext + > GetXAiMessageEFRepositoryDescriptor() + where TContext : XDbContext + { + // + var result = new XEFRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageEFRepository, + TContext + >(); + + // + return result; + } + + public static XEFRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageEFRepository, + TContext, + IXAiMessageSeeder, + TSeederImplementation + > GetXAiMessageEFRepositoryDescriptor() + where TContext : XDbContext + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XEFRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageEFRepository, + TContext, + IXAiMessageSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region Mongo Repository Descriptor Provider(s) ... + public static XMongoRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageMongoRepository + > GetXAiMessageMongoRepositoryDescriptor() + { + // + var result = new XMongoRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageMongoRepository + >(); + + // + return result; + } + + public static XMongoRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageMongoRepository, + IXAiMessageSeeder, + TSeederImplementation + > GetXAiMessageMongoRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XMongoRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageMongoRepository, + IXAiMessageSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region InMemory Repository Descriptor Provider(s) ... + public static XInMemoryRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageInMemoryRepository + > GetXAiMessageInMemoryRepositoryDescriptor() + { + // + var result = new XInMemoryRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageInMemoryRepository + >(); + + // + return result; + } + + public static XInMemoryRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageInMemoryRepository, + IXAiMessageSeeder, + TSeederImplementation + > GetXAiMessageInMemoryRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XInMemoryRepositoryDescriptor< + XAiMessage, + Guid, + IXAiMessageKeyGenerator, + XAiMessageKeyGenerator, + IXAiMessageRepositoryEvents, + XAiMessageRepositoryEvents, + XAiMessageGraphType, + GuidGraphType, + XAiMessageGraphQuery, + IXAiMessageGraphQLTypeHelper, + XAiMessageGraphQLTypeHelper, + XAiMessageGraphSchema, + IXAiMessageRepository, + XAiMessageInMemoryRepository, + IXAiMessageSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + #endregion + + // + #region XAiProject ... + // + #region EF Repository Descriptor Provider(s) ... + public static XEFRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectEFRepository, + TContext + > GetXAiProjectEFRepositoryDescriptor() + where TContext : XDbContext + { + // + var result = new XEFRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectEFRepository, + TContext + >(); + + // + return result; + } + + public static XEFRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectEFRepository, + TContext, + IXAiProjectSeeder, + TSeederImplementation + > GetXAiProjectEFRepositoryDescriptor() + where TContext : XDbContext + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XEFRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectEFRepository, + TContext, + IXAiProjectSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region Mongo Repository Descriptor Provider(s) ... + public static XMongoRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectMongoRepository + > GetXAiProjectMongoRepositoryDescriptor() + { + // + var result = new XMongoRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectMongoRepository + >(); + + // + return result; + } + + public static XMongoRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectMongoRepository, + IXAiProjectSeeder, + TSeederImplementation + > GetXAiProjectMongoRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XMongoRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectMongoRepository, + IXAiProjectSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region InMemory Repository Descriptor Provider(s) ... + public static XInMemoryRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectInMemoryRepository + > GetXAiProjectInMemoryRepositoryDescriptor() + { + // + var result = new XInMemoryRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectInMemoryRepository + >(); + + // + return result; + } + + public static XInMemoryRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectInMemoryRepository, + IXAiProjectSeeder, + TSeederImplementation + > GetXAiProjectInMemoryRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XInMemoryRepositoryDescriptor< + XAiProject, + Guid, + IXAiProjectKeyGenerator, + XAiProjectKeyGenerator, + IXAiProjectRepositoryEvents, + XAiProjectRepositoryEvents, + XAiProjectGraphType, + GuidGraphType, + XAiProjectGraphQuery, + IXAiProjectGraphQLTypeHelper, + XAiProjectGraphQLTypeHelper, + XAiProjectGraphSchema, + IXAiProjectRepository, + XAiProjectInMemoryRepository, + IXAiProjectSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Dtos\IXAiConversationDtoHub.cs + +using System; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Dtos +{ + public interface IXAiConversationDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Dtos\IXAiConversationRepositoryService.cs + +using System; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Dtos +{ + public interface IXAiConversationRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Dtos\IXAiConversationServiceProvider.cs + +using System; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xAiModels.Providers.Dtos; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Dtos +{ + public interface IXAiConversationServiceProvider : IXBaseDtoProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Dtos\IXAiMessageDtoHub.cs + +using System; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Dtos +{ + public interface IXAiMessageDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Dtos\IXAiMessageRepositoryService.cs + +using System; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Dtos +{ + public interface IXAiMessageRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Dtos\IXAiMessageServiceProvider.cs + +using System; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xAiModels.Providers.Dtos; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Dtos +{ + public interface IXAiMessageServiceProvider : IXBaseDtoProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Dtos\IXAiProjectDtoHub.cs + +using System; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Dtos +{ + public interface IXAiProjectDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Dtos\IXAiProjectRepositoryService.cs + +using System; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Dtos +{ + public interface IXAiProjectRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Dtos\IXAiProjectServiceProvider.cs + +using System; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xAiModels.Providers.Dtos; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Dtos +{ + public interface IXAiProjectServiceProvider : IXBaseDtoProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiConversationEntityHub.cs + +using System; +using xAiModels.Models.Entities; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiConversationEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiConversationGraphQLTypeHelper.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiConversationGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiConversationKeyGenerator.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiConversationKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiConversationRepository.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiConversationRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiConversationRepositoryEvents.cs + +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiConversationRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiConversationRepositoryProvider.cs + +using System; +using xAiModels.Models.Entities; +using xAiModels.Providers.Entities; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiConversationRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiConversationSeeder.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiConversationSeeder : IXBaseDbSeeder + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiMessageEntityHub.cs + +using System; +using xAiModels.Models.Entities; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiMessageEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiMessageGraphQLTypeHelper.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiMessageGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiMessageKeyGenerator.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiMessageKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiMessageRepository.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiMessageRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiMessageRepositoryEvents.cs + +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiMessageRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiMessageRepositoryProvider.cs + +using System; +using xAiModels.Models.Entities; +using xAiModels.Providers.Entities; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiMessageRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiMessageSeeder.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiMessageSeeder : IXBaseDbSeeder + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiProjectEntityHub.cs + +using System; +using xAiModels.Models.Entities; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiProjectEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiProjectGraphQLTypeHelper.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiProjectGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiProjectKeyGenerator.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiProjectKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiProjectRepository.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiProjectRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiProjectRepositoryEvents.cs + +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiProjectRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiProjectRepositoryProvider.cs + +using System; +using xAiModels.Models.Entities; +using xAiModels.Providers.Entities; +using xPushService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiProjectRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\Entities\IXAiProjectSeeder.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.Interfaces; + +namespace xAiModels.Interfaces.Entities +{ + public interface IXAiProjectSeeder : IXBaseDbSeeder + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\IXAiConversationTitleResourceProvider.cs + +using xStringService.Interfaces; + +namespace xAiModels.Interfaces +{ + public interface IXAiConversationTitleResourceProvider : IXItemResourceProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\IXAiDataProvider.cs + +namespace xAiModels.Interfaces +{ + /// + /// All Data Manipulation Requirements ... + /// + public interface IXAiDataProvider + { + + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\IXAiProjectDescriptionResourceProvider.cs + +using xStringService.Interfaces; + +namespace xAiModels.Interfaces +{ + public interface IXAiProjectDescriptionResourceProvider : IXItemResourceProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Interfaces\IXAiProjectTitleResourceProvider.cs + +using xStringService.Interfaces; + +namespace xAiModels.Interfaces +{ + public interface IXAiProjectTitleResourceProvider : IXItemResourceProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Models\Dtos\XAiConversationDto.cs + +using System; +using xModels.Base; +using xModels.Dtos; + +namespace xAiModels.Models.Dtos +{ + /// + /// Conversation Describe ... + /// + public class XAiConversationDto : XBaseGuidIDEntityDto + { + /// + /// User Identifier ... + /// + /// + public string OwnerId { get; set; } + + /// + /// Owner Info ... + /// + public virtual XPersonDto Owner { get; set; } = null; + + /// + /// Conversation Title Resource ID ... + /// + public string Title { get; set; } + + /// + /// All Exists Title Locale Resources ... + /// + public virtual XLocaleResourceDto[] TitleLocales { get; set; } + + /// + /// Created Time ... + /// + public DateTime CreatedOn { get; set; } + + /// + /// Updated Time ... + /// + public DateTime UpdatedAt { get; set; } + + /// + /// Project ID ... + /// + public Guid ProjectId { get; set; } + + /// + /// Project Title Resource ID ... + /// + public virtual string ProjectTitle { get; set; } = null; + + /// + /// Project Description Resource ID ... + /// + public virtual string ProjectDescription { get; set; } = null; + + /// + /// Project Title Locale Resources ... + /// + public virtual XLocaleResourceDto[] ProjectTitleLocales { get; set; } + + /// + /// Project Description Locale Resources ... + /// + public virtual XLocaleResourceDto[] ProjectDescriptionLocales { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Models\Dtos\XAiMessageDto.cs + +using System; +using System.Collections.Generic; +using xAiModels.Constants; +using xCommons.Extensions; +using xModels.Base; +using xModels.Dtos; + +namespace xAiModels.Models.Dtos +{ + public class XAiMessageDto : XBaseGuidIDEntityDto + { + /// + /// User Identifier ... + /// + /// + public string OwnerId { get; set; } + + /// + /// Owner Info ... + /// + public virtual XPersonDto Owner { get; set; } = null; + + /// + /// Message of Chat ... + /// + public string Content { get; set; } + + /// + /// Role of Message ... + /// + public XAiChatRole Role { get; set; } + + /// + /// Created Time ... + /// + public DateTime CreatedOn { get; set; } + + /// + /// Updated Time ... + /// + public DateTime UpdatedAt { get; set; } + + /// + /// Conversation Id ... + /// + public Guid ConversationId { get; set; } + + /// + /// Conversation Info ... + /// + public virtual XAiConversationDto Conversation { get; set; } = null; + + // + #region Meta Data Property ... + private string _metadata; + + /// + /// Meta Data of Message ... + /// + public IDictionary Metadata { get; private set; } = null; + + /// + /// Meta Data of MEssage in Json ... + /// + public string MeatDatas + { + // + get => _metadata; + + // + set + { + // + // Converts Json String to Dictionary ... + if (!value.IsNullOrEmpty()) + { + Metadata = value.FromJSON>(); + } + + // + _metadata = value; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Models\Dtos\XAiProjectDto.cs + +using System; +using System.Collections.Generic; +using xModels.Base; +using xModels.Dtos; + +namespace xAiModels.Models.Dtos +{ + /// + /// Describe a Project ... + /// + public class XAiProjectDto : XBaseGuidIDEntityDto + { + /// + /// User Identifier ... + /// + /// + public string OwnerId { get; set; } + + /// + /// Owner Info ... + /// + public virtual XPersonDto Owner { get; set; } = null; + + /// + /// Title of Project ... + /// + public string Title { get; set; } + + /// + /// Description of Project ... + /// + public string Description { get; set; } + + /// + /// a Prompt for Project Start ... + /// + public string Prompt { get; set; } + + /// + /// Created Time ... + /// + public DateTime CreatedOn { get; set; } + + /// + /// Updated Time ... + /// + public DateTime UpdatedAt { get; set; } + + /// + /// Conversations ... + /// + public virtual IList Conversations { get; set; } = []; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Models\Entities\XAiConversation.cs + +using System; +using System.ComponentModel.DataAnnotations; +using xModels.Base; + +namespace xAiModels.Models.Entities +{ + /// + /// Conversation Describe ... + /// + public class XAiConversation : XBaseGuidIDEntity + { + /// + /// User Identifier ... + /// + /// + [Required] + [StringLength(255)] + public string OwnerId { get; set; } + + /// + /// Title of Conversation ... + /// + [Required] + [StringLength(255)] + public string Title { get; set; } + + /// + /// Created Time ... + /// + public DateTime CreatedOn { get; set; } + + /// + /// Updated Time ... + /// + public DateTime UpdatedAt { get; set; } + + /// + /// Project ID ... + /// + [Required] + public Guid ProjectId { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Models\Entities\XAiMessage.cs + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using xAiModels.Constants; +using xCommons.Extensions; +using xModels.Base; + +namespace xAiModels.Models.Entities +{ + /// + /// Message Model of Ai ... + /// + public class XAiMessage : XBaseGuidIDEntity + { + /// + /// User Identifier ... + /// + /// + [Required] + [StringLength(255)] + public string OwnerId { get; set; } + + /// + /// Message of Chat ... + /// + [Required] + public string Content { get; set; } + + /// + /// Role of Message ... + /// + public XAiChatRole Role { get; set; } + + /// + /// Created Time ... + /// + public DateTime CreatedOn { get; set; } + + /// + /// Updated Time ... + /// + public DateTime UpdatedAt { get; set; } + + /// + /// Conversation Id ... + /// + [StringLength(255)] + public Guid ConversationId { get; set; } + + // + #region Meta Data Property ... + private string _metadata; + + /// + /// Meta Data of Message ... + /// + [NotMapped] + public IDictionary Metadata { get; private set; } = null; + + /// + /// Meta Data of MEssage in Json ... + /// + public string MeatDatas + { + // + get => _metadata; + + // + set + { + // + // Converts Json String to Dictionary ... + if (!value.IsNullOrEmpty()) + { + Metadata = value.FromJSON>(); + } + + // + _metadata = value; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Models\Entities\XAiProject.cs + +using System; +using System.ComponentModel.DataAnnotations; +using xModels.Base; + +namespace xAiModels.Models.Entities +{ + /// + /// Describe a Project ... + /// + public class XAiProject : XBaseGuidIDEntity + { + /// + /// User Identifier ... + /// + /// + [Required] + [StringLength(255)] + public string OwnerId { get; set; } + + /// + /// Title of Project ... + /// + [Required] + [StringLength(255)] + public string Title { get; set; } + + /// + /// Description of Project ... + /// + [StringLength(1000)] + public string Description { get; set; } + + /// + /// a Prompt for Project Start ... + /// + public string Prompt { get; set; } + + /// + /// Created Time ... + /// + public DateTime CreatedOn { get; set; } + + /// + /// Updated Time ... + /// + public DateTime UpdatedAt { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Models\XAiModelDescriptor.cs + +using xAiModels.Constants; + +namespace xAiModels.Models +{ + /// + /// Describe a Provided Model ... + /// + public class XAiModelDescriptor + { + /// + /// Provider Name ... + /// + public string Name { get; set; } + + /// + /// LLM Provider Url ... + /// + public string Url { get; set; } + + /// + /// Model Name ... + /// + public string LLM { get; set; } + + /// + /// Api Key for Connection ... + /// + public string ApiKey { get; set; } + + /// + /// Specified Model Provider Type ... + /// + public XAiModelProviderType Provider { get; set; } = XAiModelProviderType.Ollama; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Dtos\XAiConversationDtoHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xAiModels.Interfaces.Dtos; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xPushService.Base; + +namespace xAiModels.Providers.Dtos +{ + public class XAiConversationDtoHub : XBaseDtoHub, IXAiConversationDtoHub + { + public XAiConversationDtoHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Dtos\XAiConversationRepositoryService.cs + +using System; +using System.Collections.Generic; +using AutoMapper; +using xAiModels.Interfaces.Dtos; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Providers.Dtos +{ + public class XAiConversationRepositoryService : XBaseRepositoryService, IXAiConversationRepositoryService + { + public XAiConversationRepositoryService( + IXAiConversationRepository repository, + IEnumerable mapperProfiles = null + ) : base( + repository, + mapperProfiles + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Dtos\XAiConversationServiceProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xAiModels.Interfaces.Dtos; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; + +namespace xAiModels.Providers.Dtos +{ + public class XAiConversationServiceProvider : XBaseDtoProvider, IXAiConversationServiceProvider + { + public XAiConversationServiceProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXAiConversationRepositoryService service, + IXIdentityProvider identityProvider = null + ) : base( + hub, + dataConfiguration, + service, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Dtos\XAiMessageDtoHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xAiModels.Interfaces.Dtos; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xPushService.Base; + +namespace xAiModels.Providers.Dtos +{ + public class XAiMessageDtoHub : XBaseDtoHub, IXAiMessageDtoHub + { + public XAiMessageDtoHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Dtos\XAiMessageRepositoryService.cs + +using System; +using System.Collections.Generic; +using AutoMapper; +using xAiModels.Interfaces.Dtos; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Providers.Dtos +{ + public class XAiMessageRepositoryService : XBaseRepositoryService, IXAiMessageRepositoryService + { + public XAiMessageRepositoryService( + IXAiMessageRepository repository, + IEnumerable mapperProfiles = null + ) : base( + repository, + mapperProfiles + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Dtos\XAiMessageServiceProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xAiModels.Interfaces.Dtos; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; + +namespace xAiModels.Providers.Dtos +{ + public class XAiMessageServiceProvider : XBaseDtoProvider, IXAiMessageServiceProvider + { + public XAiMessageServiceProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXAiMessageRepositoryService service, + IXIdentityProvider identityProvider = null + ) : base( + hub, + dataConfiguration, + service, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Dtos\XAiProjectDtoHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xAiModels.Interfaces.Dtos; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xPushService.Base; + +namespace xAiModels.Providers.Dtos +{ + public class XAiProjectDtoHub : XBaseDtoHub, IXAiProjectDtoHub + { + public XAiProjectDtoHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Dtos\XAiProjectRepositoryService.cs + +using System; +using System.Collections.Generic; +using AutoMapper; +using xAiModels.Interfaces.Dtos; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Providers.Dtos +{ + public class XAiProjectRepositoryService : XBaseRepositoryService, IXAiProjectRepositoryService + { + public XAiProjectRepositoryService( + IXAiProjectRepository repository, + IEnumerable mapperProfiles = null + ) : base( + repository, + mapperProfiles + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Dtos\XAiProjectServiceProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xAiModels.Interfaces.Dtos; +using xAiModels.Models.Dtos; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; + +namespace xAiModels.Providers.Dtos +{ + public class XAiProjectServiceProvider : XBaseDtoProvider, IXAiProjectServiceProvider + { + public XAiProjectServiceProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXAiProjectRepositoryService service, + IXIdentityProvider identityProvider = null + ) : base( + hub, + dataConfiguration, + service, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiConversationEFRepository.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiConversationEFRepository : XBaseEFRepository, IXAiConversationRepository + where TContext : XDbContext + { + public XAiConversationEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXAiConversationKeyGenerator keyGenerator = null, + IXAiConversationRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiConversationEntityHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xPushService.Base; + +namespace xAiModels.Providers.Entities +{ + public class XAiConversationEntityHub : XBaseEntityHub, IXAiConversationEntityHub + { + public XAiConversationEntityHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiConversationInMemoryRepository.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiConversationInMemoryRepository : XBaseInMemoryRepository, IXAiConversationRepository + { + public XAiConversationInMemoryRepository( + XDataServiceConfiguration configuration, + IXAiConversationKeyGenerator keyGenerator = null, + IXAiConversationRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiConversationKeyGenerator.cs + +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiConversationKeyGenerator : XGuidKeyGenerator, IXAiConversationKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiConversationMongoRepository.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiConversationMongoRepository : XBaseMongoRepository, IXAiConversationRepository + { + public XAiConversationMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXAiConversationKeyGenerator keyGenerator = null, + IXAiConversationRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiConversationRepositoryEvents.cs + +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiConversationRepositoryEvents : XBaseRepositoryEvents, IXAiConversationRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiConversationRepositoryProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; + +namespace xAiModels.Providers.Entities +{ + public class XAiConversationRepositoryProvider : XBaseEntityProvider, IXAiConversationRepositoryProvider + { + public XAiConversationRepositoryProvider( + IHubContext hub, + IXAiConversationRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + repository, + dataConfiguration, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiConversationSeederBase.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public abstract class XAiConversationSeederBase : XBaseDbSeeder, IXAiConversationSeeder + { + protected XAiConversationSeederBase( + string entity, + string database, + IXAiConversationRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + entity, + database, + repository, + dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiMessageEFRepository.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiMessageEFRepository : XBaseEFRepository, IXAiMessageRepository + where TContext : XDbContext + { + public XAiMessageEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXAiMessageKeyGenerator keyGenerator = null, + IXAiMessageRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiMessageEntityHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xPushService.Base; + +namespace xAiModels.Providers.Entities +{ + public class XAiMessageEntityHub : XBaseEntityHub, IXAiMessageEntityHub + { + public XAiMessageEntityHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiMessageInMemoryRepository.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiMessageInMemoryRepository : XBaseInMemoryRepository, IXAiMessageRepository + { + public XAiMessageInMemoryRepository( + XDataServiceConfiguration configuration, + IXAiMessageKeyGenerator keyGenerator = null, + IXAiMessageRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiMessageKeyGenerator.cs + +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiMessageKeyGenerator : XGuidKeyGenerator, IXAiMessageKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiMessageMongoRepository.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiMessageMongoRepository : XBaseMongoRepository, IXAiMessageRepository + { + public XAiMessageMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXAiMessageKeyGenerator keyGenerator = null, + IXAiMessageRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiMessageRepositoryEvents.cs + +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiMessageRepositoryEvents : XBaseRepositoryEvents, IXAiMessageRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiMessageRepositoryProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; + +namespace xAiModels.Providers.Entities +{ + public class XAiMessageRepositoryProvider : XBaseEntityProvider, IXAiMessageRepositoryProvider + { + public XAiMessageRepositoryProvider( + IHubContext hub, + IXAiMessageRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + repository, + dataConfiguration, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiMessageSeederBase.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public abstract class XAiMessageSeederBase : XBaseDbSeeder, IXAiMessageSeeder + { + protected XAiMessageSeederBase( + string entity, + string database, + IXAiMessageRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + entity, + database, + repository, + dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiProjectEFRepository.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiProjectEFRepository : XBaseEFRepository, IXAiProjectRepository + where TContext : XDbContext + { + public XAiProjectEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXAiProjectKeyGenerator keyGenerator = null, + IXAiProjectRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiProjectEntityHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xPushService.Base; + +namespace xAiModels.Providers.Entities +{ + public class XAiProjectEntityHub : XBaseEntityHub, IXAiProjectEntityHub + { + public XAiProjectEntityHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiProjectInMemoryRepository.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiProjectInMemoryRepository : XBaseInMemoryRepository, IXAiProjectRepository + { + public XAiProjectInMemoryRepository( + XDataServiceConfiguration configuration, + IXAiProjectKeyGenerator keyGenerator = null, + IXAiProjectRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiProjectKeyGenerator.cs + +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiProjectKeyGenerator : XGuidKeyGenerator, IXAiProjectKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiProjectMongoRepository.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiProjectMongoRepository : XBaseMongoRepository, IXAiProjectRepository + { + public XAiProjectMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXAiProjectKeyGenerator keyGenerator = null, + IXAiProjectRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiProjectRepositoryEvents.cs + +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public class XAiProjectRepositoryEvents : XBaseRepositoryEvents, IXAiProjectRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiProjectRepositoryProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; + +namespace xAiModels.Providers.Entities +{ + public class XAiProjectRepositoryProvider : XBaseEntityProvider, IXAiProjectRepositoryProvider + { + public XAiProjectRepositoryProvider( + IHubContext hub, + IXAiProjectRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + repository, + dataConfiguration, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\Entities\XAiProjectSeederBase.cs + +using System; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xAiModels.Providers.Entities +{ + public abstract class XAiProjectSeederBase : XBaseDbSeeder, IXAiProjectSeeder + { + protected XAiProjectSeederBase( + string entity, + string database, + IXAiProjectRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + entity, + database, + repository, + dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiConversationGraphQLTypeHelper.cs + +using System; +using xAiModels.Constants; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.GraphQL; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiConversationGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXAiConversationGraphQLTypeHelper + { + public override string GetInQueryCollectionName() + { + return XAiModelsConstants.XAiConversationCollectionName; + } + + public override string GetInQuerySingleName() + { + return XAiModelsConstants.XAiConversationSignleName; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiConversationGraphQuery.cs + +using System; +using GraphQL.Types; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.GraphQL; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiConversationGraphQuery : XBaseGraphQLQuery + { + public XAiConversationGraphQuery( + XDataServiceConfiguration configuration, + IXAiConversationRepository repository, + IXAiConversationGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiConversationGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiConversationGraphSchema : Schema + { + public XAiConversationGraphSchema(IServiceProvider services) : base(services) + { + Query = (XAiConversationGraphQuery)services.GetService(typeof(XAiConversationGraphQuery)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiConversationGraphType.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.GraphQL; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiConversationGraphType : XBaseGraphObjectType + { + public XAiConversationGraphType() : base() + { + Field(x => x.Title); + Field(x => x.OwnerId); + Field(x => x.CreatedOn); + Field(x => x.UpdatedAt); + Field(x => x.ProjectId); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiMessageGraphQLTypeHelper.cs + +using System; +using xAiModels.Constants; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.GraphQL; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiMessageGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXAiMessageGraphQLTypeHelper + { + public override string GetInQueryCollectionName() + { + return XAiModelsConstants.XAiMessageCollectionName; + } + + public override string GetInQuerySingleName() + { + return XAiModelsConstants.XAiMessageSingleName; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiMessageGraphQuery.cs + +using System; +using GraphQL.Types; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.GraphQL; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiMessageGraphQuery : XBaseGraphQLQuery + { + public XAiMessageGraphQuery( + XDataServiceConfiguration configuration, + IXAiMessageRepository repository, + IXAiMessageGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiMessageGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiMessageGraphSchema : Schema + { + public XAiMessageGraphSchema(IServiceProvider services) : base(services) + { + Query = (XAiMessageGraphQuery)services.GetService(typeof(XAiMessageGraphQuery)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiMessageGraphType.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.GraphQL; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiMessageGraphType: XBaseGraphObjectType + { + public XAiMessageGraphType() : base() + { + Field(x => x.Role); + Field(x => x.OwnerId); + Field(x => x.Content); + Field(x => x.Metadata); + Field(x => x.CreatedOn); + Field(x => x.UpdatedAt); + Field(x => x.ConversationId); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiProjectGraphQLTypeHelper.cs + +using System; +using xAiModels.Constants; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.GraphQL; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiProjectGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXAiProjectGraphQLTypeHelper + { + public override string GetInQueryCollectionName() + { + return XAiModelsConstants.XAiProjectCollectionName; + } + + public override string GetInQuerySingleName() + { + return XAiModelsConstants.XAiProjectSingleName; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiProjectGraphQuery.cs + +using System; +using GraphQL.Types; +using xAiModels.Interfaces.Entities; +using xAiModels.Models.Entities; +using xDataService.Configuration; +using xDataService.GraphQL; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiProjectGraphQuery : XBaseGraphQLQuery + { + public XAiProjectGraphQuery( + XDataServiceConfiguration configuration, + IXAiProjectRepository repository, + IXAiProjectGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiProjectGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiProjectGraphSchema : Schema + { + public XAiProjectGraphSchema(IServiceProvider services) : base(services) + { + Query = (XAiProjectGraphQuery)services.GetService(typeof(XAiProjectGraphQuery)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\GraphQL\XAiProjectGraphType.cs + +using System; +using xAiModels.Models.Entities; +using xDataService.GraphQL; + +namespace xAiModels.Providers.GraphQL +{ + public class XAiProjectGraphType : XBaseGraphObjectType + { + public XAiProjectGraphType() : base() + { + Field(x => x.Title); + Field(x => x.OwnerId); + Field(x => x.Prompt); + Field(x => x.CreatedOn); + Field(x => x.UpdatedAt); + Field(x => x.Description); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\XAiConversationEntityRegisterar.cs + +using System; +using Microsoft.EntityFrameworkCore; +using xAiModels.Configurations.Entities; +using xAiModels.Models.Entities; +using xDataService.Interfaces; +using xDataService.Providers; + +namespace xAiModels.Providers +{ + public class XAiConversationEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar + { + public XAiConversationEntityRegisterar() + : base(nameof(XAiConversationEntityRegisterar)) + { + // + // Add Entity ... + AddEntity(); + } + + /// + /// Configure Entities ... + /// + /// + public override void ConfigureEntities(ModelBuilder modelBuilder) + { + // + // Configure Entity Using Provided ... + modelBuilder + .ApplyConfigurationsFromAssembly(typeof(XAiConversationEntityConfiguration).Assembly); + + // + base.ConfigureEntities(modelBuilder); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\XAiConversationTitleResourceProvider.cs + +using xAiModels.Constants; +using xAiModels.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Providers; + +namespace xAiModels.Providers +{ + public class XAiConversationTitleResourceProvider : XBaseItemResourceProvider, IXAiConversationTitleResourceProvider + { + public XAiConversationTitleResourceProvider( + IXStringServiceProvider stringProvider + ) : base( + stringProvider: stringProvider, + item: XAiModelsConstants.XAiConversationTitleIdentifier + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\XAiDataProvider.cs + +using Microsoft.AspNetCore.SignalR; +using xAiModels.Interfaces; +using xAiModels.Interfaces.Entities; +using xAiModels.Providers.Dtos; +using xAiModels.Providers.Entities; + +namespace xAiModels.Providers +{ + /// + /// All Data Manipulation Requirements ... + /// + public class XAiDataProvider : IXAiDataProvider + { + private readonly IHubContext messageDtoHub; + private readonly IHubContext projectDtoHub; + private readonly IXAiMessageRepository messageRepositoryService; + private readonly IXAiProjectRepository projectRepositoryService; + private readonly IHubContext messageEntityHub; + private readonly IHubContext projectEntityHub; + private readonly IXAiConversationRepository conversationRepository; + private readonly IHubContext conversationDtoHub; + private readonly IHubContext conversationEntityHub; + private readonly IXAiProjectTitleResourceProvider projectTitleResourceProvider; + private readonly IXAiConversationTitleResourceProvider conversationTitleResourceProvider; + private readonly IXAiProjectDescriptionResourceProvider projectDescriptionResourceProvider; + + public XAiDataProvider( + IHubContext messageDtoHub, + IHubContext projectDtoHub, + IXAiMessageRepository messageRepositoryService, + IXAiProjectRepository projectRepositoryService, + IHubContext messageEntityHub, + IHubContext projectEntityHub, + IXAiConversationRepository conversationRepository, + IHubContext conversationDtoHub, + IHubContext conversationEntityHub, + IXAiProjectTitleResourceProvider projectTitleResourceProvider, + IXAiConversationTitleResourceProvider conversationTitleResourceProvider, + IXAiProjectDescriptionResourceProvider projectDescriptionResourceProvider + ) + { + this.messageDtoHub = messageDtoHub; + this.projectDtoHub = projectDtoHub; + this.projectEntityHub = projectEntityHub; + this.messageEntityHub = messageEntityHub; + this.conversationDtoHub = conversationDtoHub; + this.conversationEntityHub = conversationEntityHub; + this.conversationRepository = conversationRepository; + this.messageRepositoryService = messageRepositoryService; + this.projectRepositoryService = projectRepositoryService; + this.projectTitleResourceProvider = projectTitleResourceProvider; + this.conversationTitleResourceProvider = conversationTitleResourceProvider; + this.projectDescriptionResourceProvider = projectDescriptionResourceProvider; + } + + + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\XAiMessageEntityRegisterar.cs + +using System; +using Microsoft.EntityFrameworkCore; +using xAiModels.Configurations.Entities; +using xAiModels.Models.Entities; +using xDataService.Interfaces; +using xDataService.Providers; + +namespace xAiModels.Providers +{ + public class XAiMessageEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar + { + public XAiMessageEntityRegisterar() + : base(nameof(XAiMessageEntityRegisterar)) + { + // + // Add Entity ... + AddEntity(); + } + + /// + /// Configure Entities ... + /// + /// + public override void ConfigureEntities(ModelBuilder modelBuilder) + { + // + // Configure Entity Using Provided ... + modelBuilder + .ApplyConfigurationsFromAssembly(typeof(XAiMessageEntityConfiguration).Assembly); + + // + base.ConfigureEntities(modelBuilder); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\XAiProjectDescriptionResourceProvider.cs + +using xAiModels.Constants; +using xAiModels.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Providers; + +namespace xAiModels.Providers +{ + public class XAiProjectDescriptionResourceProvider : XBaseItemResourceProvider, IXAiProjectDescriptionResourceProvider + { + public XAiProjectDescriptionResourceProvider( + IXStringServiceProvider stringProvider + ) : base( + stringProvider: stringProvider, + item: XAiModelsConstants.XAiProjectDescriptionIdentifier + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\XAiProjectEntityRegisterar.cs + +using System; +using Microsoft.EntityFrameworkCore; +using xAiModels.Configurations.Entities; +using xAiModels.Models.Entities; +using xDataService.Interfaces; +using xDataService.Providers; + +namespace xAiModels.Providers +{ + public class XAiProjectEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar + { + public XAiProjectEntityRegisterar() + : base(nameof(XAiProjectEntityRegisterar)) + { + // + // Add Entity ... + AddEntity(); + } + + /// + /// Configure Entities ... + /// + /// + public override void ConfigureEntities(ModelBuilder modelBuilder) + { + // + // Configure Entity Using Provided ... + modelBuilder + .ApplyConfigurationsFromAssembly(typeof(XAiProjectEntityConfiguration).Assembly); + + // + base.ConfigureEntities(modelBuilder); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiModels\Providers\XAiProjectTitleResourceProvider.cs + +using xAiModels.Constants; +using xAiModels.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Providers; + +namespace xAiModels.Providers +{ + public class XAiProjectTitleResourceProvider : XBaseItemResourceProvider, IXAiProjectTitleResourceProvider + { + public XAiProjectTitleResourceProvider( + IXStringServiceProvider stringProvider + ) : base( + stringProvider: stringProvider, + item: XAiModelsConstants.XAiProjectTitleIdentifier + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Configurations\XAIServiceConfiguration.cs + +namespace xAiService.Configurations +{ + public partial class XAIServiceConfiguration + { + public string Url { get; set; } + public string XPoweredValue { get; set; } + public int DefaultClientTimeout { get; set; } = 360; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Constants\ConfigurationNodeNames.cs + +namespace xAiService.Constants +{ + public partial struct ConfigurationNodeNames + { + public const string AI_SERVICE_NODE = "AIServiceConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Constants\XAiApiConstants.cs + +namespace xAiService.Constants +{ + public struct XAiApiQueryParams + { + public const string Prompt = "prompt"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Constants\XAiApiEndpoint.cs + +using xExceptions.Attributes; + +namespace xAiService.Constants +{ + public enum XAiApiEndpoint + { + [StringValue("AskAI")] + Ask, + [StringValue("AskAIStream")] + Stream, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\DI\XDIHelperExtension.cs + +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xAiService.Configurations; +using xAiService.Constants; +using xAiService.Extensions; +using xAiService.Interfaces; +using xAiService.Services; +using xCommons.Extensions; +using xExceptions.Constants; + +namespace xAiService.DI +{ + public static partial class XDIHelperExtension + { + /// + /// Extract AI Service Configuration ... + /// + /// + /// + public static XAIServiceConfiguration GetXAIServiceConfiguration(this IConfiguration source) + { + // + XAIServiceConfiguration result = null; + + // + var section = source.GetSection(ConfigurationNodeNames.AI_SERVICE_NODE); + if (!section.IsNull()) + { + result = section.Get(); + } + + // + return result; + } + + /// + /// Add AI Service Configuration ... + /// + /// + /// + public static void AddXAIServiceConfiguration( + this IServiceCollection source, + XAIServiceConfiguration configuration + ) + { + source.AddSingleton(configuration); + } + + /// + /// Adding XAiService ... + /// + /// + /// + /// + public static void AddXAIService( + this IServiceCollection services, + IConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + var config = configuration.GetXAIServiceConfiguration(); + AddAIService( + services, + config, + lifeTime + ); + } + + /// + /// Adding XAiService ... + /// + /// + /// + /// + public static void AddXAIService( + this IServiceCollection services, + XAIServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + AddAIService( + services, + configuration, + lifeTime + ); + } + + // + #region Private ... + /// + /// a LogTag for Service ... + /// + private static string XLogTag = "XFileService"; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Adding Ai Service ... + /// + /// + /// + /// + private static void AddAIService( + this IServiceCollection services, + XAIServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Singleton + ) + { + // + var exception = XException.InvalidArgs.ToException(); ; + + // + // Services ... + var isServicesExists = !services.IsNull(); + if (!isServicesExists) + { + // + Log("AddAIService failed, services not provided ..."); + throw exception; + } + + // + // Lifetime ... + var isLifetimeExists = !lifeTime.IsNull(); + if (!isLifetimeExists) + { + // + Log("AddAIService failed, lifetime not provided ..."); + throw exception; + } + + // + // Check Dependencies Exists ... + var isDependenciesPassed = true; + if (!isDependenciesPassed) + { + // + Log("AddAIService failed due Dependencies Issues, not provided ..."); + throw exception; + } + + // + // Validate Configurations ... + var isConfigurationExists = + !configuration.IsNull() && + configuration.IsValid(); + if (!isConfigurationExists) + { + // + Log("AddAIService failed due InValid Service Configuration or not provided ..."); + throw exception; + } + + // + // Register Configurations ... + services.AddXAIServiceConfiguration(configuration); + + // + // Register Services ... + services.Add(new ServiceDescriptor(typeof(IXAiService), typeof(XAiService), lifeTime)); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Extensions\ConfigurationExtensions.cs + +using xAiService.Configurations; +using xCommons.Extensions; + +namespace xAiService.Extensions +{ + public static class ModelExtensions + { + /// + /// Validate XAiServiceConfiguration ... + /// + /// + /// + public static bool IsValid(this XAIServiceConfiguration source) + { + // + bool result = + !source.IsNull() && + !source.Url.IsNullOrEmpty() && + source.Url.IsValidUrl(); + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Extensions\PromptExtensions.cs + +using System.Collections.Generic; +using System.Linq; +using xAiService.Models.Dtos; +using xCommons.Extensions; + +namespace xAiService.Extensions +{ + public static class PromptExtensions + { + /// + /// Get Template Params Filled Prompt ... + /// + /// + /// + /// + public static string GetMessage( + this XPromptDto source, + IDictionary @params = null + ) + { + // + var result = string.Empty; + + // + if (!source.IsNullOrDefault() && + !source.Prompt.IsNullOrEmpty() + ) + { + result = source.Prompt; + } + + // + // Check Params Conditions ... + if (!source.Params.IsNullOrDefault() && + source.Params.HasChild() && + !@params.IsNullOrDefault() && + @params.HasChild() + ) + { + // + // Select Params ... + var existsParams = source.Params + .Where(p => @params.Any(pr => pr.Key == p.Name)) + .Distinct() + .ToList(); + + // + // Replace Selected Params ... + existsParams + .ForEach(pk => + { + result = result.Replace(pk.Param, @params[pk.Name]); + }); + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Helpers\XPromptHelper.cs + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using xAiService.Models.Dtos; +using xCommons.Extensions; + +namespace xAiService.Helpers +{ + public static class XPromptHelper + { + /// + /// Holds Initialized Prompts ... + /// + private static XPromptsDto prompts; + + /// + /// Initialize Prompt Helper ... + /// + /// + /// + /// + public static void Initialize(string path = "prompts.json") + { + // + // Create full Path and Check File Exists ... + var fullPath = Path.Combine(AppContext.BaseDirectory, path); + if (!File.Exists(fullPath)) + { + throw new Exception($"Prompts Resources not found: {fullPath}"); + } + + // + // Read File as Json Text ... + var json = File.ReadAllText(fullPath); + var options = new JsonSerializerOptions + { + AllowTrailingCommas = true, + PropertyNameCaseInsensitive = true, + ReadCommentHandling = JsonCommentHandling.Skip, + Converters = + { + new JsonStringEnumConverter( + namingPolicy: null, + allowIntegerValues: false + ) + } + }; + + // + // Serialized Root Object ... + var root = JsonSerializer.Deserialize(json, options); + if (!root.IsNullOrDefault()) + { + prompts = new XPromptsDto { Prompts = root.Prompts }; + } + } + + /// + /// Retrieve all Allowed Prompts as Enumerable ... + /// + /// + public static IEnumerable GetPrompts() + { + return prompts!.Prompts.AsEnumerable(); + } + + /// + /// Get Specified Prompt ... + /// + /// + /// + /// + public static XPromptDto GetPrompt(string name) + { + // + return GetPrompts() + .FirstOrDefault(p => p.Name == name); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Interfaces\IXAiService.cs + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.AI; +using xAiService.Configurations; +using xIdentityModels.Models; +using xIdentityService.Interfaces; + +namespace xAiService.Interfaces +{ + public interface IXAiService : IXBaseIdentityHttpProvider + { + /// + /// XAiService Configurations ... + /// + /// + XAIServiceConfiguration Configuration { get; } + + // + #region Text Actions ... + /// + /// Generated Response ... + /// + /// + /// + /// + /// + public Task GetResponseAsync( + string prompt, + XUserClaimsInfoDto userInfo, + CancellationToken cancellationToken = default + ); + + /// + /// Generated Response ... + /// + /// + /// + /// + /// + public Task GetTextResponseAsync( + string prompt, + XUserClaimsInfoDto userInfo, + CancellationToken cancellationToken = default + ); + + /// + /// Generate Text Response Stream ... + /// + /// + /// + /// + /// + Task GetTextReponseStreamAsync( + string prompt, + XUserClaimsInfoDto userInfo, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Interfaces\IXAiServiceControllerBase.cs + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace xAiService.Interfaces +{ + public interface IXAiServiceControllerBase + { + Task> Ask( + [FromQuery] string prompt, + CancellationToken cancellationToken = default + ); + + Task AskStream( + [FromQuery] string prompt, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Models\Dtos\XPromptsDto.cs + +namespace xAiService.Models.Dtos +{ + /// + /// Described Prompts in app ... + /// + public class XPromptsDto + { + public XPromptDto[] Prompts { get; set; } = []; + } + + /// + /// a Single Prompt Description ... + /// + public class XPromptDto + { + /// + /// Prompt Name ... + /// + public string Name { get; set; } + + /// + /// Prompt Template Message ... + /// + public string Prompt { get; set; } + + /// + /// Prompt Description ... + /// + public string Description { get; set; } + + /// + /// Describe Prompt Params ... + /// + public virtual XPromptParamDto[] Params { get; set; } = null; + } + + /// + /// Prompt Param used to Fill dynamic Datas to Specified Prompts ... + /// + public class XPromptParamDto + { + /// + /// Param Name ... + /// + public string Name { get; set; } + + /// + /// Parma Identifier ... + /// + public string Param { get; set; } + + /// + /// Param Description ... + /// + public string Description { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xAiService\Services\XAiService.cs + +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.Logging; +using xAiService.Configurations; +using xAiService.Constants; +using xAiService.Interfaces; +using xCommons.Providers; +using xHttpService.Constants; +using xIdentityModels.Models; +using xIdentityService.Interfaces; +using xIdentityService.Providers; + +namespace xAiService.Services +{ + public class XAiService : XBaseIdentityHttpProvider, IXAiService + { + /// + /// XAiService Configurations ... + /// + /// + public XAIServiceConfiguration Configuration { get; } + + public XAiService( + ILogger logger, + IXTokenProvider tokenProvider, + IXIdentityProvider identityProvider, + XAIServiceConfiguration configuration, + XValidationProvider validationProvider + ) : base( + configuration.Url, + logger, + configuration.XPoweredValue, + configuration.DefaultClientTimeout, + tokenProvider, + identityProvider, + validationProvider + ) + { } + + // + #region Text Actions ... + /// + /// Generated Response ... + /// + /// + /// + /// + /// + public Task GetResponseAsync( + string prompt, + XUserClaimsInfoDto userInfo, + CancellationToken cancellationToken = default + ) + { + throw new System.NotImplementedException(); + } + + /// + /// Generated Response ... + /// + /// + /// + /// + /// + public async Task GetTextResponseAsync( + string prompt, + XUserClaimsInfoDto userInfo, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(prompt); + + // + var client = GetRestClient(userInfo); + var request = GetRestRequet( + endpoint: XAiApiEndpoint.Ask, + addXPoweredValue: true, + queryStrings: new Dictionary + { + { XAiApiQueryParams.Prompt, prompt } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.GET, + cancellationToken: cancellationToken + ); + + // + return response; + } + + /// + /// Generate Text Response Stream ... + /// + /// + /// + /// + /// + public async Task GetTextReponseStreamAsync( + string prompt, + XUserClaimsInfoDto userInfo, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(prompt) + .AddNotNull(userInfo) + .AddNotZero(userInfo.ExpiresAt) + .AddNotEmpty(userInfo.AccessToken) + .AddNotEmpty(userInfo.RefreshToken) + .ValidateGroup(); + + // + var tokens = ToTokenResponse(userInfo); + var result = await StreamData( + tokens: tokens, + addXPoweredValue: true, + endpoint: XAiApiEndpoint.Stream, + cancellationToken: cancellationToken, + queryStrings: new Dictionary + { + { XAiApiQueryParams.Prompt, prompt } + } + ); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Configurations\Entities\XCategoryEntityConfiguration.cs + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xCategoryService.Constants; +using xCategoryService.Models.Entities; +using xDataService.Providers; + +namespace xCategoryService.Configurations.Entities +{ + public class XCategoryEntityConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable(XCategoryServiceConstants.XCategoryTable); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Configurations\Entities\XSubCategoryEntityConfiguration.cs + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xCategoryService.Constants; +using xCategoryService.Models.Entities; +using xDataService.Providers; + +namespace xCategoryService.Configurations.Entities +{ + public class XSubCategoryEntityConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable(XCategoryServiceConstants.XSubCategoryTable); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Configurations\XCategoryServiceConfiguration.cs + +using xCategoryService.Models.Dtos; + +namespace xCategoryService.Configurations +{ + /// + /// a Configuration Class for Providing Data to Configure XCategoryService ... + /// + public class XCategoryServiceConfiguration + { + /// + /// a List of Categories for Seeding ... + /// + public XCategoryResourceDto[] Categories { get; set; } = []; + + /// + /// a List of SubCategories for Seeding ... + /// + public XSubCategoryResourceDto[] SubCategories { get; set; } = []; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Constants\ConfigurationNodeNames.cs + +namespace xCategoryService.Constants +{ + public partial struct ConfigurationNodeNames + { + public const string CATEGORY_SERVICE_NODE = "CategoryServiceConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Constants\XCategoryServiceConstants.cs + +namespace xCategoryService.Constants +{ + public struct XCategoryServiceConstants + { + // + // Service Extensions Log Tag ... + public const string XCategoryServiceDILogTag = "XCategoryService"; + + // + // Table Mapping Identifiers ... + public const string XCategoryTable = "Categories"; + public const string XSubCategoryTable = "SubCategories"; + + // + // GraphQL Collection Mappings ... + public const string XCategorySingleName = "category"; + public const string XCategoryCollectionName = "categories"; + public const string XSubCategorySingleName = "subCategory"; + public const string XSubCategoryCollectionName = "subCategories"; + + // + // Hubs ... + public const string XCategoryDtoHub = "categoryDto"; + public const string XCategoryEntityHub = "categoryEntity"; + public const string XSubCategoryDtoHub = "subCategoryDto"; + public const string XSubCategoryEntityHub = "subCategoryEntity"; + + // + // Custome Constants ... + public const string SubCategoryReference = "SubCat"; + public const string CategoryTitleResourceIdentifier = "CatT"; + public const string CategoryDescriptionResourceIdentifier = "CatD"; + public const string SubCategoryTitleResourceIdentifier = "SubCatT"; + public const string SubCategoryDescriptionResourceIdentifier = "SubCatD"; + + // + // EF Repositories Helper Extensions ... + public const string GetXCategoryEFRepositoryDescriptor = "GetXCategoryEFRepositoryDescriptor"; + public const string GetXSubCategoryEFRepositoryDescriptor = "GetXSubCategoryEFRepositoryDescriptor"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Controllers\XCategoryProviderControllerBase.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Dtos; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityService.Interfaces; +using xModels.Dtos; +using xPushService.Controllers; + +namespace xCategoryService.Controllers +{ + public abstract class XCategoryProviderControllerBase : XBaseProvidedController, IXCategoryProviderController + { + private readonly IXCategoryProvider provider; + + protected XCategoryProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXCategoryProvider provider, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider, + provider, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { + this.provider = provider; + } + + // + #region Overrides ... + [NonAction] + [HttpGet("{id}")] + public override async Task> Get( + [FromRoute] int id, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.Get( + id: id, + useDefaultIncludes: useDefaultIncludes, + cancellationToken: cancellationToken + ); + } + + [NonAction] + [HttpGet("")] + public override async Task>> GetAll( + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.GetAll( + useDefaultOrders: useDefaultOrders, + cancellationToken: cancellationToken, + useDefaultIncludes: useDefaultIncludes + ); + } + + [NonAction] + [HttpGet("Find/One")] + public override async Task> FindOne( + [FromRoute] string query, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.FindOne( + query: query, + cancellationToken: cancellationToken, + useDefaultIncludes: useDefaultIncludes + ); + } + + [NonAction] + [HttpGet("Find/Many")] + public override async Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.FindMany( + query: query, + useDefaultOrders: useDefaultOrders, + cancellationToken: cancellationToken, + useDefaultIncludes: useDefaultIncludes + ); + } + + [NonAction] + [HttpGet("Query")] + public override async Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.Query( + query: query, + useDefaultOrders: useDefaultOrders, + cancellationToken: cancellationToken, + useDefaultIncludes: useDefaultIncludes + ); + } + + [NonAction] + [HttpGet("Query/Owned")] + public override async Task>> QueryOwned( + [FromQuery] XQuery query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.QueryOwned( + query: query, + useDefaultOrders: useDefaultOrders, + cancellationToken: cancellationToken, + useDefaultIncludes: useDefaultIncludes + ); + } + + [NonAction] + [HttpPost("")] + public override async Task> Add( + [FromBody] XCategoryDto item, + CancellationToken cancellationToken = default + ) + { + // + return await base.Add( + item: item, + cancellationToken: cancellationToken + ); + } + + [NonAction] + [HttpPut("{id}")] + public override async Task> Update( + [FromRoute] int id, + [FromBody] XCategoryDto item, + CancellationToken cancellationToken = default + ) + { + // + return await base.Update( + id: id, + item: item, + cancellationToken: cancellationToken + ); + } + + [NonAction] + [HttpDelete("{id}")] + public override async Task> Remove( + [FromRoute] int id, + CancellationToken cancellationToken = default + ) + { + // + return await base.Remove( + id: id, + cancellationToken: cancellationToken + ); + } + #endregion + + // + #region Actions ... + /// + /// Check Has Specified Locale or not ... + /// + /// + /// + /// + /// + [HttpGet("Resources/{id}/HasLocale")] + public virtual async Task> HasLocale( + [FromRoute] int id, + [FromQuery] string language, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid || + !id.IsValidIntId()) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotEmpty(language); + + // + var result = await provider.HasLocale( + id: id, + language: language, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Load an Item by all of it's Related Translations ... + /// + /// + /// + /// + [HttpGet("Resources/{id}")] + public virtual async Task> GetResource( + [FromRoute] int id, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid || + !id.IsValidIntId()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await provider.GetResource( + id: id, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add Category Item for Specified Language ... + /// + /// + /// + /// + /// + [HttpPost("Resources")] + public virtual async Task> Add( + [FromBody] XCategoryDto item, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(item) + .AddNotEmpty(language) + .AddNotEmpty(item.Title) + .AddNotEmpty(item.Description) + .ValidateGroupAsync(); + + // + // Reading Requirements ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Add( + item: item, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add Category Item for by It's Locales ... + /// + /// + /// + /// + [HttpPost("Resources/AddResource")] + public virtual async Task> AddResource( + [FromBody] XCategoryResourceDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(item) + .AddNotNull(item.Titles) + .AddNotZeroChilds(item.Titles.Locales) + .ValidateGroupAsync(); + + // + // Reading Requirements ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.AddResource( + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Item for Specified Language ... + /// + /// + /// if null, Remove All + /// + /// + [HttpDelete("Resources/{id}")] + public virtual async Task> Remove( + [FromRoute] int id, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid || + !id.IsValidIntId()) + { + XException.InvalidArgs.Throw(); + } + + // + // Reading Requirements ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Remove( + id: id, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update a Category Item for Specified Language ... + /// + /// + /// + /// + /// + /// + [HttpPut("Resources/{id}")] + public virtual async Task> Update( + [FromRoute] int id, + [FromBody] XCategoryDto item, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid || + !id.IsValidIntId()) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(item) + .AddNotEmpty(language) + .AddNotEmpty(item.Title) + .AddNotEmpty(item.Description) + .ValidateGroupAsync(); + + // + // Reading Requirements ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Update( + id: id, + item: item, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Refresh a Resource ... + /// + /// + /// + /// + [HttpPost("Resources/Refresh")] + public virtual async Task> Refresh( + [FromBody] XCategoryResourceDto resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(resource) + .AddNotNull(resource.Item) + .ValidateGroupAsync(); + + // + // Reading Requirements ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Refresh( + resource: resource, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get All Resources ... + /// + /// + /// + [HttpGet("Resources/All")] + public virtual async Task>> GetAllResources( + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await provider.GetAllResources( + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find One Resource ... + /// + /// + /// + /// + [HttpGet("Resources/Find/One")] + public virtual async Task> FindOneResource( + [FromQuery] string query, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(query) + .ValidateGroupAsync(); + + // + var result = await provider.FindOneResource( + cancellationToken: cancellationToken, + predicate: x => x.PropValuesContains(query) + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find Many Resource ... + /// + /// + /// + /// + [HttpGet("Resources/Find/Many")] + public virtual async Task>> FindManyResource( + [FromQuery] string query, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(query) + .ValidateGroupAsync(); + + // + var result = await provider.FindManyResource( + cancellationToken: cancellationToken, + predicate: x => x.PropValuesContains(query), + orderBuilder: x => x.OrderBy(y => y.Item.Id) + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Resources by Query Pattern ... + /// + /// + /// + /// + [HttpGet("Resources/Query")] + public virtual async Task>> QueryResources( + [FromQuery] XQuery query = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(query) + .ValidateGroupAsync(); + + // + var result = await provider.QueryResources( + query: query, + predicate: null, + cancellationToken: cancellationToken, + orderBuilder: x => x.OrderBy(y => y.Item.Id) + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Controllers\XCategoryRepositoryControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xCommons.Configurations; +using xCommons.Providers; +using xDataService.Controllers; +using xDataService.Interfaces; + +namespace xCategoryService.Controllers +{ + public abstract class XCategoryRepositoryControllerBase : XBaseRepositoryController, IXBaseRepositoryController + { + protected XCategoryRepositoryControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXCategoryRepository repository, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repository, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Controllers\XCategoryRepositoryProviderControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Entities; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xPushService.Interfaces; + +namespace xCategoryService.Controllers +{ + public abstract class XCategoryRepositoryProviderControllerBase : XBaseRepositoryProviderController, IXBaseRepositoryProviderController + { + protected XCategoryRepositoryProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXCategoryRepositoryProvider 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\xCategoryService\Controllers\XCategoryServiceControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCommons.Configurations; +using xCommons.Providers; +using xDataService.Controllers; +using xDataService.Interfaces; + +namespace xCategoryService.Controllers +{ + public abstract class XCategoryServiceControllerBase : XBaseServiceController, IXBaseServiceController + { + protected XCategoryServiceControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXCategoryRepositoryService repositoryService, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repositoryService, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Controllers\XCategoryServiceProviderControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Dtos; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xPushService.Interfaces; + +namespace xCategoryService.Controllers +{ + public abstract class XCategoryServiceProviderControllerBase : XBaseServiceProviderController, IXBaseServiceProviderController + { + protected XCategoryServiceProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXCategoryServiceProvider 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\xCategoryService\Controllers\XSubCategoryProviderControllerBase.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Dtos; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityService.Interfaces; +using xModels.Dtos; +using xPushService.Controllers; + +namespace xCategoryService.Controllers +{ + public abstract class XSubCategoryProviderControllerBase : XBaseProvidedController, IXSubCategoryProviderController + { + private readonly IXSubCategoryProvider provider; + + protected XSubCategoryProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXSubCategoryProvider provider, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider, + provider, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { + this.provider = provider; + } + + // + #region Overrides ... + [NonAction] + [HttpGet("{id}")] + public override async Task> Get( + [FromRoute] int id, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.Get( + id: id, + useDefaultIncludes: useDefaultIncludes, + cancellationToken: cancellationToken + ); + } + + [NonAction] + [HttpGet("")] + public override async Task>> GetAll( + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.GetAll( + useDefaultOrders: useDefaultOrders, + cancellationToken: cancellationToken, + useDefaultIncludes: useDefaultIncludes + ); + } + + [NonAction] + [HttpGet("Find/One")] + public override async Task> FindOne( + [FromRoute] string query, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.FindOne( + query: query, + cancellationToken: cancellationToken, + useDefaultIncludes: useDefaultIncludes + ); + } + + [NonAction] + [HttpGet("Find/Many")] + public override async Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.FindMany( + query: query, + useDefaultOrders: useDefaultOrders, + cancellationToken: cancellationToken, + useDefaultIncludes: useDefaultIncludes + ); + } + + [NonAction] + [HttpGet("Query")] + public override async Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.Query( + query: query, + useDefaultOrders: useDefaultOrders, + cancellationToken: cancellationToken, + useDefaultIncludes: useDefaultIncludes + ); + } + + [NonAction] + [HttpGet("Query/Owned")] + public override async Task>> QueryOwned( + [FromQuery] XQuery query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + return await base.QueryOwned( + query: query, + useDefaultOrders: useDefaultOrders, + cancellationToken: cancellationToken, + useDefaultIncludes: useDefaultIncludes + ); + } + + [NonAction] + [HttpPost("")] + public override async Task> Add( + [FromBody] XSubCategoryDto item, + CancellationToken cancellationToken = default + ) + { + // + return await base.Add( + item: item, + cancellationToken: cancellationToken + ); + } + + [NonAction] + [HttpPut("{id}")] + public override async Task> Update( + [FromRoute] int id, + [FromBody] XSubCategoryDto item, + CancellationToken cancellationToken = default + ) + { + // + return await base.Update( + id: id, + item: item, + cancellationToken: cancellationToken + ); + } + + [NonAction] + [HttpDelete("{id}")] + public override async Task> Remove( + [FromRoute] int id, + CancellationToken cancellationToken = default + ) + { + // + return await base.Remove( + id: id, + cancellationToken: cancellationToken + ); + } + #endregion + + // + #region Actions ... + /// + /// Check Has Specified Locale or not ... + /// + /// + /// + /// + /// + [HttpGet("Resources/{id}/HasLocale")] + public virtual async Task> HasLocale( + [FromRoute] int id, + [FromQuery] string language, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid || + !id.IsValidIntId()) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotEmpty(language); + + // + var result = await provider.HasLocale( + id: id, + language: language, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Load an Item by all of it's Related Translations ... + /// + /// + /// + /// + [HttpGet("Resources/{id}")] + public virtual async Task> GetResource( + [FromRoute] int id, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid || + !id.IsValidIntId()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await provider.GetResource( + id: id, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add Category Item for Specified Language ... + /// + /// + /// + /// + /// + [HttpPost("Resources")] + public virtual async Task> Add( + [FromBody] XSubCategoryDto item, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(item) + .AddNotEmpty(language) + .AddNotEmpty(item.Title) + .AddNotEmpty(item.Description) + .ValidateGroupAsync(); + + // + // Reading Requirements ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Add( + item: item, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add Item by it's Locales and Reference ... + /// + /// + /// + /// + [HttpPost("Resources/AddResource")] + public virtual async Task> AddResource( + [FromBody] XSubCategoryResourceDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(item) + .AddNotNull(item.Titles) + .AddNotZeroChilds(item.Titles.Locales) + .ValidateGroupAsync(); + + // + // Reading Requirements ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.AddResource( + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Item for Specified Language ... + /// + /// + /// if null, Remove All + /// + /// + [HttpDelete("Resources/{id}")] + public virtual async Task> Remove( + [FromRoute] int id, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid || + !id.IsValidIntId()) + { + XException.InvalidArgs.Throw(); + } + + // + // Reading Requirements ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Remove( + id: id, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update a Category Item for Specified Language ... + /// + /// + /// + /// + /// + /// + [HttpPut("Resources/{id}")] + public virtual async Task> Update( + [FromRoute] int id, + [FromBody] XSubCategoryDto item, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid || + !id.IsValidIntId()) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(item) + .AddNotEmpty(language) + .AddNotEmpty(item.Title) + .AddNotEmpty(item.Description) + .ValidateGroupAsync(); + + // + // Reading Requirements ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Update( + id: id, + item: item, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Refresh a Resource ... + /// + /// + /// + /// + [HttpPost("Resources/Refresh")] + public virtual async Task> Refresh( + [FromBody] XSubCategoryResourceDto resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(resource) + .AddNotNull(resource.Item) + .ValidateGroupAsync(); + + // + // Reading Requirements ... + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Refresh( + resource: resource, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get All Resources ... + /// + /// + /// + [HttpGet("Resources/All")] + public virtual async Task>> GetAllResources( + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await provider.GetAllResources( + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find One Resource ... + /// + /// + /// + /// + [HttpGet("Resources/Find/One")] + public virtual async Task> FindOneResource( + [FromQuery] string query, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(query) + .ValidateGroupAsync(); + + // + var result = await provider.FindOneResource( + cancellationToken: cancellationToken, + predicate: x => x.PropValuesContains(query) + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find Many Resource ... + /// + /// + /// + /// + [HttpGet("Resources/Find/Many")] + public virtual async Task>> FindManyResource( + [FromQuery] string query, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(query) + .ValidateGroupAsync(); + + // + var result = await provider.FindManyResource( + cancellationToken: cancellationToken, + predicate: x => x.PropValuesContains(query), + orderBuilder: x => x.OrderBy(y => y.Item.Id) + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Resources by Query Pattern ... + /// + /// + /// + /// + [HttpGet("Resources/Query")] + public virtual async Task>> QueryResources( + [FromQuery] XQuery query = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(query) + .ValidateGroupAsync(); + + // + var result = await provider.QueryResources( + query: query, + predicate: null, + cancellationToken: cancellationToken, + orderBuilder: x => x.OrderBy(y => y.Item.Id) + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Controllers\XSubCategoryRepositoryControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xCommons.Configurations; +using xCommons.Providers; +using xDataService.Controllers; +using xDataService.Interfaces; + +namespace xCategoryService.Controllers +{ + public abstract class XSubCategoryRepositoryControllerBase : XBaseRepositoryController, IXBaseRepositoryController + { + protected XSubCategoryRepositoryControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXSubCategoryRepository repository, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repository, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Controllers\XSubCategoryRepositoryProviderControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Entities; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xPushService.Interfaces; + +namespace xCategoryService.Controllers +{ + public abstract class XSubCategoryRepositoryProviderControllerBase : XBaseRepositoryProviderController, IXBaseRepositoryProviderController + { + protected XSubCategoryRepositoryProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXSubCategoryRepositoryProvider 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\xCategoryService\Controllers\XSubCategoryServiceControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCommons.Configurations; +using xCommons.Providers; +using xDataService.Controllers; +using xDataService.Interfaces; + +namespace xCategoryService.Controllers +{ + public abstract class XSubCategoryServiceControllerBase : XBaseServiceController, IXBaseServiceController + { + protected XSubCategoryServiceControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXSubCategoryRepositoryService repositoryService, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repositoryService, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Controllers\XSubCategoryServiceProviderControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Dtos; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xPushService.Interfaces; + +namespace xCategoryService.Controllers +{ + public abstract class XSubCategoryServiceProviderControllerBase : XBaseServiceProviderController, IXBaseServiceProviderController + { + protected XSubCategoryServiceProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXSubCategoryServiceProvider 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\xCategoryService\DI\XDIHelperExtension.cs + +using System; +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCategoryService.Configurations; +using xCategoryService.Constants; +using xCategoryService.Helpers; +using xCategoryService.Interfaces; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Providers; +using xCategoryService.Providers.Dtos; +using xCategoryService.Providers.Entities; +using xCommons.Extensions; +using xCommons.Helpers; +using xDataService.Constants; +using xDataService.Db; +using xDataService.DI; +using xDataService.Models; +using xExceptions.Constants; +using xPushService.DI; +using xPushService.Helpers; + +namespace xCategoryService.DI +{ + public static class XDIHelperExtension + { + /// + /// Extract Service Configurations ... + /// + /// + /// + public static XCategoryServiceConfiguration GetXCategoryServiceConfigurations( + this IConfiguration source + ) + { + // + XCategoryServiceConfiguration result = null; + + // + var section = source.GetSection(Constants.ConfigurationNodeNames.CATEGORY_SERVICE_NODE); + var sectionJson = source.GetSectionAsJson(Constants.ConfigurationNodeNames.CATEGORY_SERVICE_NODE); + if (!section.IsNull()) + { + result = sectionJson.FromJSON(); + } + + // + return result; + } + + /// + /// Register Service Configuration ... + /// + /// + /// + public static void AddXCategoryServiceConfiguration( + this IServiceCollection source, + IConfiguration configuration + ) + { + // + // Extract Configuration Class ... + var config = configuration.GetXCategoryServiceConfigurations(); + + // + // Register Class as Service ... + source.AddXCategoryServiceConfiguration(config); + } + + /// + /// Register Service Configuration ... + /// + /// + /// + public static void AddXCategoryServiceConfiguration( + this IServiceCollection source, + XCategoryServiceConfiguration configuration + ) + { + // + // Normalize Configuration Class ... + if (configuration.IsNull()) + { + configuration = new XCategoryServiceConfiguration(); + } + + // + // Register ... + source.AddSingleton(configuration); + } + + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXCategoryService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + AddCategoryService( + contextType: null, + services: services, + lifeTime: lifeTime, + repositoryType: repositoryType + ); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + public static void AddXCategoryService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + where TContext : XDbContext + { + AddCategoryService( + services: services, + lifeTime: lifeTime, + contextType: typeof(TContext), + repositoryType: repositoryType + ); + } + + /// + /// Use XSubCategoryService Middleware ... + /// + /// + /// + public static void UseXCategoryService( + this IApplicationBuilder app, + bool isDevelopmentEnvironment = false + ) + { + // + #region Using Hubs ... + // + var pushHelper = new XPushServiceHelper(); + + // + // Category ... + pushHelper.AddHub(XCategoryServiceConstants.XCategoryDtoHub); + pushHelper.AddHub(XCategoryServiceConstants.XCategoryEntityHub); + + // + // SubCategory ... + pushHelper.AddHub(XCategoryServiceConstants.XSubCategoryDtoHub); + pushHelper.AddHub(XCategoryServiceConstants.XSubCategoryEntityHub); + + // + app.UseXPushService(pushHelper); + #endregion + + // + // Using XCategory Repository Descriptor ... + if (!categoryDescriptor.IsNull()) + { + // + app.UseXRepository( + descriptor: categoryDescriptor, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + + // + // Using XSubCategory Repository Descriptor ... + if (!subCategoryDescriptor.IsNull()) + { + // + app.UseXRepository( + descriptor: subCategoryDescriptor, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + + // + // Create an Scope for Services Acces ... + // Handle Seeding Category/SubCategory Resources ... + using (var scope = app.ApplicationServices.CreateScope()) + { + // + // Retrieve Configuratuions ... + var categoryProvider = scope.ServiceProvider.GetService(); + var subCategoryProvider = scope.ServiceProvider.GetService(); + var configuration = scope.ServiceProvider.GetService(); + if (!configuration.IsNullOrDefault() && + !categoryProvider.IsNullOrDefault() && + !subCategoryDescriptor.IsNullOrDefault() + ) + { + // + // Logging ... + Log("Start Seeding ..."); + + // + // Count Data in Category ... + var hasCategory = categoryProvider.Count().GetAwaiter().GetResult() > 0; + var hasSubCategory = subCategoryProvider.Count().GetAwaiter().GetResult() > 0; + + // + // Seed Category Resources ... + var canSeed = + !hasCategory && + configuration.Categories.HasChild(); + if (canSeed) + { + // + // Loop through Provided items ... + foreach (var cat in configuration.Categories) + { + // + try + { + // + Log($"Start Seeding Category: {cat.Titles.Locales.ToArray()[0].Value} ..."); + + // + categoryProvider.Refresh( + resource: cat, + userInfo: null, + connectionId: null, + cancellationToken: default + ) + .GetAwaiter() + .GetResult(); + + // + Log($"Seeding Category: {cat.Titles.Locales.ToArray()[0].Value} Finished Successfully ..."); + } + catch (Exception ex) + { + Log($"Seeding Category: {cat.Titles.Locales.ToArray()[0].Value} Failed due: {ex.Message} ..."); + } + } + } + + // + // Seed SubCategory Resources ... + canSeed = + !hasSubCategory && + configuration.SubCategories.HasChild(); + if (canSeed) + { + // + // Loop through Provided items ... + foreach (var subcat in configuration.SubCategories) + { + // + try + { + // + Log($"Start Seeding SubCategory: {subcat.Titles.Locales.ToArray()[0].Value} ..."); + + // + subCategoryProvider.Refresh( + userInfo: null, + resource: subcat, + connectionId: null, + cancellationToken: default + ) + .GetAwaiter() + .GetResult(); + + // + Log($"Seeding SubCategory: {subcat.Titles.Locales.ToArray()[0].Value} Finished Successfully ..."); + } + catch (Exception ex) + { + Log($"Seeding SubCategory: {subcat.Titles.Locales.ToArray()[0].Value} Failed due: {ex.Message} ..."); + } + } + } + + // + Log("Finish Seeding ..."); + } + } + } + + // + #region Private ... + private static XRepositoryDescriptor categoryDescriptor = null; + private static XRepositoryDescriptor subCategoryDescriptor = null; + + /// + /// a LogTag for Service ... + /// + private static string XLogTag = XCategoryServiceConstants.XCategoryServiceDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + private static void AddCategoryService( + 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: + // + // Category ... + categoryDescriptor = typeof(XCategoryServiceHelper) + .InvokeGenericMethod( + args: null, + runtimeType: contextType, + methodName: XCategoryServiceConstants.GetXCategoryEFRepositoryDescriptor + ); + + // + // SubCategory ... + subCategoryDescriptor = typeof(XCategoryServiceHelper) + .InvokeGenericMethod( + args: null, + runtimeType: contextType, + methodName: XCategoryServiceConstants.GetXSubCategoryEFRepositoryDescriptor + ); + break; + + // + case XRepositoryType.Mongo: + // + // Category ... + categoryDescriptor = XCategoryServiceHelper.GetXCategoryMongoRepositoryDescriptor(); + + // + // SubCategory ... + subCategoryDescriptor = XCategoryServiceHelper.GetXSubCategoryMongoRepositoryDescriptor(); + break; + + // + case XRepositoryType.InMemory: + // + // Category ... + categoryDescriptor = XCategoryServiceHelper.GetXCategoryInMemoryRepositoryDescriptor(); + + // + // SubCategory ... + subCategoryDescriptor = XCategoryServiceHelper.GetXSubCategoryInMemoryRepositoryDescriptor(); + break; + } + + // + // Register Category Repository Descriptor ... + if (!categoryDescriptor.IsNull()) + { + // + services.AddXRepository( + lifeTime: lifeTime, + descriptor: categoryDescriptor + ); + } + + // + // Register SubCategory Repository Descriptor ... + if (!subCategoryDescriptor.IsNull()) + { + // + services.AddXRepository( + lifeTime: lifeTime, + descriptor: subCategoryDescriptor + ); + } + + // + #region Register Services ... + // + #region Category ... + // + // Models ... + services.Add( + new ServiceDescriptor(typeof(IXCategoryServiceProvider), typeof(XCategoryServiceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXCategoryRepositoryService), typeof(XCategoryRepositoryService), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXCategoryRepositoryProvider), typeof(XCategoryRepositoryProvider), lifeTime) + ); + + // + // Resource Providers ... + services.Add( + new ServiceDescriptor(typeof(IXCategoryTitleResourceProvider), typeof(XCategoryTitleResourceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXCategoryDescriptionResourceProvider), typeof(XCategoryDescriptionResourceProvider), lifeTime) + ); + + // + // Reference Providers ... + services.Add( + new ServiceDescriptor(typeof(IXCategorySubCategoryReferenceProvider), typeof(XCategorySubCategoryReferenceProvider), lifeTime) + ); + + // + // Provider ... + services.Add( + new ServiceDescriptor(typeof(IXCategoryProvider), typeof(XCategoryProvider), lifeTime) + ); + #endregion + + // + #region SubCategory ... + // + // Models ... + services.Add( + new ServiceDescriptor(typeof(IXSubCategoryServiceProvider), typeof(XSubCategoryServiceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXSubCategoryRepositoryService), typeof(XSubCategoryRepositoryService), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXSubCategoryRepositoryProvider), typeof(XSubCategoryRepositoryProvider), lifeTime) + ); + + // + // Resource Providers ... + services.Add( + new ServiceDescriptor(typeof(IXSubCategoryTitleResourceProvider), typeof(XSubCategoryTitleResourceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXSubCategoryDescriptionResourceProvider), typeof(XSubCategoryDescriptionResourceProvider), lifeTime) + ); + + // + // Provider ... + services.Add( + new ServiceDescriptor(typeof(IXSubCategoryProvider), typeof(XSubCategoryProvider), lifeTime) + ); + #endregion + #endregion + + // + // Register Service Main Provider ... + services.Add( + new ServiceDescriptor(typeof(IXCategoryService), typeof(XCategoryService), lifeTime) + ); + + // + Log("Service Regitration Succeed ..."); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Extensions\XCategoryResourceExtensions.cs + +using System.Linq; +using xCategoryService.Models.Dtos; +using xCategoryService.Providers.Dtos; +using xCommons.Extensions; + +namespace xCategoryService.Extensions +{ + /// + /// Extensions Method Related to XCategoryResourceDto ... + /// + public static class XCategoryResourceExtensions + { + /// + /// Extract Available Languages ... + /// + /// + /// + public static string[] ExtractLanguages( + this XCategoryResourceDto source + ) + { + // + var result = new string[] { }; + + // + // Validate ... + if (!source.IsNullOrDefault()) + { + return result; + } + + // + result = + source.Titles.Locales.Select(x => x.Language) + .Union(source.Descriptions.Locales.Select(x => x.Language)) + .Distinct() + .ToArray(); + + // + return result; + } + + /// + /// Check Has Locale or not ... + /// + /// + /// + /// + public static bool HasLocale( + this XCategoryResourceDto source, + string language + ) + { + // + var result = + !language.IsNullOrEmpty() && + !source.IsNullOrDefault() && + source.Titles.Locales + .Any(l => l.Language + .Equals( + language, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Get Item Translation ... + /// + /// + /// + /// + public static XCategoryDto GetTranslate( + this XCategoryResourceDto source, + string language + ) + { + // + XCategoryDto result = null; + + // + var isValid = source + .HasLocale(language); + if (!isValid) + { + return result; + } + + // + var titleLocale = source.Titles.Locales + .FirstOrDefault(l => l.Language + .Equals( + language, + System.StringComparison.InvariantCultureIgnoreCase)); + var descriptionLocale = source.Descriptions.Locales + .FirstOrDefault(l => l.Language + .Equals( + language, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + isValid = !titleLocale.IsNullOrDefault(); + if (!isValid) + { + return result; + } + + // + result = source.Item; + result.Title = titleLocale.Value; + result.Description = + descriptionLocale.IsNullOrDefault() + ? string.Empty + : descriptionLocale.Value; + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Extensions\XModelExtensions.cs + +using System.Collections.Generic; +using System.Linq; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCommons.Extensions; +using xDataService.Extensions; +using xDataService.Models; + +namespace xCategoryService.Extensions +{ + public static class XModelExtensions + { + /// + /// Extract References of a Model ... + /// + /// + /// + public static IList> GetReferences( + this XCategory 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 XCategoryDto 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 XCategory UpdateReferences( + this XCategory source, + IList> references, + bool forceClean = true + ) + { + // + XCategory 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 XCategoryDto UpdateReferences( + this XCategoryDto source, + IList> references, + bool forceClean = true + ) + { + // + XCategoryDto 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\xCategoryService\Extensions\XSubCategoryResourceExtensions.cs + +using System.Linq; +using xCategoryService.Models.Dtos; +using xCategoryService.Providers.Dtos; +using xCommons.Extensions; + +namespace xCategoryService.Extensions +{ + /// + /// Extensions Method Related to XSubCategoryResourceDto ... + /// + public static class XSubCategoryResourceExtensions + { + /// + /// Extract Available Languages ... + /// + /// + /// + public static string[] ExtractLanguages( + this XSubCategoryResourceDto source + ) + { + // + var result = new string[] { }; + + // + // Validate ... + if (!source.IsNullOrDefault()) + { + return result; + } + + // + result = + source.Titles.Locales.Select(x => x.Language) + .Union(source.Descriptions.Locales.Select(x => x.Language)) + .Distinct() + .ToArray(); + + // + return result; + } + + /// + /// Check Has Locale or not ... + /// + /// + /// + /// + public static bool HasLocale( + this XSubCategoryResourceDto source, + string language + ) + { + // + var result = + !language.IsNullOrEmpty() && + !source.IsNullOrDefault() && + source.Titles.Locales + .Any(l => l.Language + .Equals( + language, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Get Item Translation ... + /// + /// + /// + /// + public static XSubCategoryDto GetTranslate( + this XSubCategoryResourceDto source, + string language + ) + { + // + XSubCategoryDto result = null; + + // + var isValid = source + .HasLocale(language); + if (!isValid) + { + return result; + } + + // + var titleLocale = source.Titles.Locales + .FirstOrDefault(l => l.Language + .Equals( + language, + System.StringComparison.InvariantCultureIgnoreCase)); + var descriptionLocale = source.Descriptions.Locales + .FirstOrDefault(l => l.Language + .Equals( + language, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + isValid = !titleLocale.IsNullOrDefault(); + if (!isValid) + { + return result; + } + + // + result = source.Item; + result.Title = titleLocale.Value; + result.Description = + descriptionLocale.IsNullOrDefault() + ? string.Empty + : descriptionLocale.Value; + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Helpers\XCategoryServiceHelper.cs + +using GraphQL.Types; +using xCategoryService.Interfaces; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Entities; +using xCategoryService.Providers.GraphQL; +using xDataService.Db; +using xDataService.Models; +using xDataService.Providers; + +namespace xCategoryService.Helpers +{ + public static class XCategoryServiceHelper + { + // + #region EF Repository Descriptor Provider(s) ... + // + #region Category + public static XEFRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryEFRepository, + TContext + > GetXCategoryEFRepositoryDescriptor() + where TContext : XDbContext + { + // + var result = new XEFRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryEFRepository, + TContext + >(); + + // + return result; + } + + public static XEFRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryEFRepository, + TContext, + IXCategorySeeder, + TSeederImplementation + > GetXCategoryEFRepositoryDescriptor() + where TContext : XDbContext + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XEFRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryEFRepository, + TContext, + IXCategorySeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region SubCategory + public static XEFRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryEFRepository, + TContext + > GetXSubCategoryEFRepositoryDescriptor() + where TContext : XDbContext + { + // + var result = new XEFRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryEFRepository, + TContext + >(); + + // + return result; + } + + public static XEFRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryEFRepository, + TContext, + IXSubCategorySeeder, + TSeederImplementation + > GetXSubCategoryEFRepositoryDescriptor() + where TContext : XDbContext + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XEFRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryEFRepository, + TContext, + IXSubCategorySeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + #endregion + + // + #region Mongo Repository Descriptor Provider(s) ... + // + #region Category + public static XMongoRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryMongoRepository + > GetXCategoryMongoRepositoryDescriptor() + { + // + var result = new XMongoRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryMongoRepository + >(); + + // + return result; + } + + public static XMongoRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryMongoRepository, + IXCategorySeeder, + TSeederImplementation + > GetXCategoryMongoRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XMongoRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryMongoRepository, + IXCategorySeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region SubCategory + public static XMongoRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryMongoRepository + > GetXSubCategoryMongoRepositoryDescriptor() + { + // + var result = new XMongoRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryMongoRepository + >(); + + // + return result; + } + + public static XMongoRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryMongoRepository, + IXSubCategorySeeder, + TSeederImplementation + > GetXSubCategoryMongoRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XMongoRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryMongoRepository, + IXSubCategorySeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + #endregion + + // + #region InMemory Repository Descriptor Provider(s) ... + // + #region Category + public static XInMemoryRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryInMemoryRepository + > GetXCategoryInMemoryRepositoryDescriptor() + { + // + var result = new XInMemoryRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryInMemoryRepository + >(); + + // + return result; + } + + public static XInMemoryRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryInMemoryRepository, + IXCategorySeeder, + TSeederImplementation + > GetXCategoryInMemoryRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XInMemoryRepositoryDescriptor< + XCategory, + int, + IXCategoryKeyGenerator, + XCategoryKeyGenerator, + IXCategoryRepositoryEvents, + XCategoryRepositoryEvents, + XCategoryGraphType, + IntGraphType, + XCategoryGraphQuery, + IXCategoryGraphQLTypeHelper, + XCategoryGraphQLTypeHelper, + XCategoryGraphSchema, + IXCategoryRepository, + XCategoryInMemoryRepository, + IXCategorySeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region SubCategory + public static XInMemoryRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryInMemoryRepository + > GetXSubCategoryInMemoryRepositoryDescriptor() + { + // + var result = new XInMemoryRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryInMemoryRepository + >(); + + // + return result; + } + + public static XInMemoryRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryInMemoryRepository, + IXSubCategorySeeder, + TSeederImplementation + > GetXSubCategoryInMemoryRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XInMemoryRepositoryDescriptor< + XSubCategory, + int, + IXSubCategoryKeyGenerator, + XSubCategoryKeyGenerator, + IXSubCategoryRepositoryEvents, + XSubCategoryRepositoryEvents, + XSubCategoryGraphType, + IntGraphType, + XSubCategoryGraphQuery, + IXSubCategoryGraphQLTypeHelper, + XSubCategoryGraphQLTypeHelper, + XSubCategoryGraphSchema, + IXSubCategoryRepository, + XSubCategoryInMemoryRepository, + IXSubCategorySeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Dtos\IXCategoryDtoHub.cs + +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces.Dtos +{ + public interface IXCategoryDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Dtos\IXCategoryRepositoryService.cs + +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Dtos +{ + public interface IXCategoryRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Dtos\IXCategoryServiceProvider.cs + +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Dtos; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces.Dtos +{ + public interface IXCategoryServiceProvider : IXBaseDtoProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Dtos\IXSubCategoryDtoHub.cs + +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces.Dtos +{ + public interface IXSubCategoryDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Dtos\IXSubCategoryRepositoryService.cs + +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Dtos +{ + public interface IXSubCategoryRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Dtos\IXSubCategoryServiceProvider.cs + +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Dtos; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces.Dtos +{ + public interface IXSubCategoryServiceProvider : IXBaseDtoProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXCategoryEntityHub.cs + +using xCategoryService.Models.Entities; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXCategoryEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXCategoryGraphQLTypeHelper.cs + +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXCategoryGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXCategoryKeyGenerator.cs + +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXCategoryKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXCategoryRepository.cs + +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXCategoryRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXCategoryRepositoryEvents.cs + +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXCategoryRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXCategoryRepositoryProvider.cs + +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Entities; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXCategoryRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXCategorySeeder.cs + +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXCategorySeeder : IXBaseDbSeeder + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXSubCategoryEntityHub.cs + +using xCategoryService.Models.Entities; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXSubCategoryEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXSubCategoryGraphQLTypeHelper.cs + +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXSubCategoryGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXSubCategoryKeyGenerator.cs + +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXSubCategoryKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXSubCategoryRepository.cs + +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXSubCategoryRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXSubCategoryRepositoryEvents.cs + +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXSubCategoryRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXSubCategoryRepositoryProvider.cs + +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Entities; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXSubCategoryRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\Entities\IXSubCategorySeeder.cs + +using xCategoryService.Models.Entities; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces.Entities +{ + public interface IXSubCategorySeeder : IXBaseDbSeeder + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXBaseCategoryProvider.cs + +using xCategoryService.Models.Dtos; +using xDataService.Interfaces; + +namespace xCategoryService.Interfaces +{ + public interface IXBaseCategoryProvider : IXBaseReferencedProvider + {} +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXCategoryDescriptionResourceProvider.cs + +using xStringService.Interfaces; + +namespace xCategoryService.Interfaces +{ + public interface IXCategoryDescriptionResourceProvider : IXItemResourceProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXCategoryProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Dtos; +using xIdentityModels.Models; +using xModels.Dtos; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces +{ + public interface IXCategoryProvider : IXBaseProvided, IXCategorySubCategoryReferenceProvider + { + /// + /// Check Has Specified Locale or not ... + /// + /// + /// + /// + /// + Task HasLocale( + int id, + string language, + CancellationToken cancellationToken = default + ); + + /// + /// Load an Item by all of it's Related Translations ... + /// + /// + /// + /// + Task GetResource( + int id, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + Task Add( + XCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item by It's Locales ... + /// + /// + /// + /// + /// + /// + Task AddResource( + XCategoryResourceDto item, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Item for Specified Language ... + /// + /// + /// if null, Remove All + /// + /// + /// + /// + Task Remove( + int id, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + /// + Task Update( + int id, + XCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Refresh a Resource ... + /// + /// + /// + /// + /// + /// + Task Refresh( + XCategoryResourceDto resource, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Resources ... + /// + /// + /// + Task> GetAllResources( + CancellationToken cancellationToken = default + ); + + /// + /// Find One Resource ... + /// + /// + /// + /// + Task FindOneResource( + Expression> predicate = null, + CancellationToken cancellationToken = default + ); + + /// + /// Find Many Resource ... + /// + /// + /// + /// + /// + Task> FindManyResource( + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Resources by Query Pattern ... + /// + /// + /// + /// + /// + /// + Task> QueryResources( + XQuery query = null, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Resources as Async Enumerable ... + /// + /// + /// + IAsyncEnumerable GetAllResourcesAsAsyncEnumerable( + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXCategoryProviderController.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Dtos; +using xModels.Dtos; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces +{ + public interface IXCategoryProviderController : IXBaseProvidedController + { + /// + /// Check Has Specified Locale or not ... + /// + /// + /// + /// + /// + [HttpGet("Resources/{id}/HasLocale")] + Task> HasLocale( + [FromRoute] int id, + [FromQuery] string language, + CancellationToken cancellationToken = default + ); + + /// + /// Load an Item by all of it's Related Translations ... + /// + /// + /// + /// + [HttpGet("Resources/{id}")] + Task> GetResource( + [FromRoute] int id, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item for Specified Language ... + /// + /// + /// + /// + /// + [HttpPost("Resources")] + Task> Add( + [FromBody] XCategoryDto item, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item by It's Locales ... + /// + /// + /// + /// + [HttpPost("Resources/AddResource")] + Task> AddResource( + [FromBody] XCategoryResourceDto item, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Item for Specified Language ... + /// + /// + /// if null, Remove All + /// + /// + [HttpDelete("Resources/{id}")] + Task> Remove( + [FromRoute] int id, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Item for Specified Language ... + /// + /// + /// + /// + /// + /// + [HttpPut("Resources/{id}")] + Task> Update( + [FromRoute] int id, + [FromBody] XCategoryDto item, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Refresh a Resource ... + /// + /// + /// + /// + [HttpPost("Resources/Refresh")] + Task> Refresh( + [FromBody] XCategoryResourceDto resource, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Resources ... + /// + /// + /// + [HttpGet("Resources/All")] + Task>> GetAllResources( + CancellationToken cancellationToken = default + ); + + /// + /// Find One Resource ... + /// + /// + /// + /// + [HttpGet("Resources/Find/One")] + Task> FindOneResource( + [FromQuery] string query, + CancellationToken cancellationToken = default + ); + + /// + /// Find Many Resource ... + /// + /// + /// + /// + [HttpGet("Resources/Find/Many")] + Task>> FindManyResource( + [FromQuery] string query, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Resources by Query Pattern ... + /// + /// + /// + /// + [HttpGet("Resources/Query")] + Task>> QueryResources( + [FromQuery] XQuery query = null, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXCategoryService.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using xCategoryService.Models.Dtos; +using xIdentityModels.Models; +using xModels.Dtos; + +namespace xCategoryService.Interfaces +{ + /// + /// Completely Exported Service ... + /// + public interface IXCategoryService + { + // + #region Category Provider ... + /// + /// Check Has Specified Locale or not ... + /// + /// + /// + /// + /// + Task CategoryHasLocale( + int id, + string language, + CancellationToken cancellationToken = default + ); + + /// + /// Load an Item by all of it's Related Translations ... + /// + /// + /// + /// + Task GetCategoryResource( + int id, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + Task AddCategory( + XCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item by It's Locales ... + /// + /// + /// + /// + /// + /// + Task AddCategoryResource( + XCategoryResourceDto item, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Item for Specified Language ... + /// + /// + /// if null, Remove All + /// + /// + /// + /// + Task RemoveCategory( + int id, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + /// + Task UpdateCategory( + int id, + XCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Refresh a Resource ... + /// + /// + /// + /// + /// + /// + Task RefreshCategory( + XCategoryResourceDto resource, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Resources ... + /// + /// + /// + Task> GetAllCategoryResources( + CancellationToken cancellationToken = default + ); + + /// + /// Find One Resource ... + /// + /// + /// + /// + Task FindOneCategoryResource( + Expression> predicate = null, + CancellationToken cancellationToken = default + ); + + /// + /// Find Many Resource ... + /// + /// + /// + /// + /// + Task> FindManyCategoryResource( + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Resources by Query Pattern ... + /// + /// + /// + /// + /// + /// + Task> QueryCategoryResources( + XQuery query = null, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Resources as Async Enumerable ... + /// + /// + /// + IAsyncEnumerable GetAllCategoryResourcesAsAsyncEnumerable( + CancellationToken cancellationToken = default + ); + #endregion + + // + #region SubCategory Provider ... + /// + /// Check Has Specified Locale or not ... + /// + /// + /// + /// + /// + Task SubCategoryHasLocale( + int id, + string language, + CancellationToken cancellationToken = default + ); + + /// + /// Load an Item by all of it's Related Translations ... + /// + /// + /// + /// + Task GetSubCategoryResource( + int id, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + Task AddSubCategory( + XSubCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item by it's Locales and Reference ... + /// + /// + /// + /// + /// + /// + Task AddSubCategoryResource( + XSubCategoryResourceDto item, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Item for Specified Language ... + /// + /// + /// if null, Remove All + /// + /// + /// + /// + Task RemoveSubCategory( + int id, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + /// + Task UpdateSubCategory( + int id, + XSubCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Refresh a Resource ... + /// + /// + /// + /// + /// + /// + Task RefreshSubCategory( + XSubCategoryResourceDto resource, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Resources ... + /// + /// + /// + Task> GetAllSubCategoryResources( + CancellationToken cancellationToken = default + ); + + /// + /// Find One Resource ... + /// + /// + /// + /// + Task FindOneSubCategoryResource( + Expression> predicate = null, + CancellationToken cancellationToken = default + ); + + /// + /// Find Many Resource ... + /// + /// + /// + /// + /// + Task> FindManySubCategoryResource( + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Resources by Query Pattern ... + /// + /// + /// + /// + /// + /// + Task> QuerySubCategoryResources( + XQuery query = null, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Resources as Async Enumerable ... + /// + /// + /// + IAsyncEnumerable GetAllSubCategoryResourcesAsAsyncEnumerable( + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXCategorySubCategoryReferenceProvider.cs + +namespace xCategoryService.Interfaces +{ + /// + /// a Service for SubCategory Referencing on Category ... + /// + public interface IXCategorySubCategoryReferenceProvider : IXBaseCategoryProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXCategoryTitleResourceProvider.cs + +using xStringService.Interfaces; + +namespace xCategoryService.Interfaces +{ + public interface IXCategoryTitleResourceProvider : IXItemResourceProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXSubCategoryDescriptionResourceProvider.cs + +using xStringService.Interfaces; + +namespace xCategoryService.Interfaces +{ + public interface IXSubCategoryDescriptionResourceProvider : IXItemResourceProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXSubCategoryProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Dtos; +using xIdentityModels.Models; +using xModels.Dtos; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces +{ + public interface IXSubCategoryProvider : IXBaseProvided + { + /// + /// Check Has Specified Locale or not ... + /// + /// + /// + /// + /// + Task HasLocale( + int id, + string language, + CancellationToken cancellationToken = default + ); + + /// + /// Load an Item by all of it's Related Translations ... + /// + /// + /// + /// + Task GetResource( + int id, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + Task Add( + XSubCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item by it's Locales and Reference ... + /// + /// + /// + /// + /// + /// + Task AddResource( + XSubCategoryResourceDto item, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Item for Specified Language ... + /// + /// + /// if null, Remove All + /// + /// + /// + /// + Task Remove( + int id, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + /// + Task Update( + int id, + XSubCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Refresh a Resource ... + /// + /// + /// + /// + /// + /// + Task Refresh( + XSubCategoryResourceDto resource, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Resources ... + /// + /// + /// + Task> GetAllResources( + CancellationToken cancellationToken = default + ); + + /// + /// Find One Resource ... + /// + /// + /// + /// + Task FindOneResource( + Expression> predicate = null, + CancellationToken cancellationToken = default + ); + + /// + /// Find Many Resource ... + /// + /// + /// + /// + /// + Task> FindManyResource( + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Resources by Query Pattern ... + /// + /// + /// + /// + /// + /// + Task> QueryResources( + XQuery query = null, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Resources as Async Enumerable ... + /// + /// + /// + IAsyncEnumerable GetAllResourcesAsAsyncEnumerable( + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXSubCategoryProviderController.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xCategoryService.Providers.Dtos; +using xModels.Dtos; +using xPushService.Interfaces; + +namespace xCategoryService.Interfaces +{ + public interface IXSubCategoryProviderController : IXBaseProvidedController + { + /// + /// Check Has Specified Locale or not ... + /// + /// + /// + /// + /// + [HttpGet("Resources/{id}/HasLocale")] + Task> HasLocale( + [FromRoute] int id, + [FromQuery] string language, + CancellationToken cancellationToken = default + ); + + /// + /// Load an Item by all of it's Related Translations ... + /// + /// + /// + /// + [HttpGet("Resources/{id}")] + Task> GetResource( + [FromRoute] int id, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item for Specified Language ... + /// + /// + /// + /// + /// + [HttpPost("Resources")] + Task> Add( + [FromBody] XSubCategoryDto item, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Item by it's Locales and Reference ... + /// + /// + /// + /// + [HttpPost("Resources/AddResource")] + Task> AddResource( + [FromBody] XSubCategoryResourceDto item, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Item for Specified Language ... + /// + /// + /// if null, Remove All + /// + /// + [HttpDelete("Resources/{id}")] + Task> Remove( + [FromRoute] int id, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Item for Specified Language ... + /// + /// + /// + /// + /// + /// + [HttpPut("Resources/{id}")] + Task> Update( + [FromRoute] int id, + [FromBody] XSubCategoryDto item, + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Refresh a Resource ... + /// + /// + /// + /// + [HttpPost("Resources/Refresh")] + Task> Refresh( + [FromBody] XSubCategoryResourceDto resource, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Resources ... + /// + /// + /// + [HttpGet("Resources/All")] + Task>> GetAllResources( + CancellationToken cancellationToken = default + ); + + /// + /// Find One Resource ... + /// + /// + /// + /// + [HttpGet("Resources/Find/One")] + Task> FindOneResource( + [FromQuery] string query, + CancellationToken cancellationToken = default + ); + + /// + /// Find Many Resource ... + /// + /// + /// + /// + [HttpGet("Resources/Find/Many")] + Task>> FindManyResource( + [FromQuery] string query, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Resources by Query Pattern ... + /// + /// + /// + /// + [HttpGet("Resources/Query")] + Task>> QueryResources( + [FromQuery] XQuery query = null, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Interfaces\IXSubCategoryTitleResourceProvider.cs + +using xStringService.Interfaces; + +namespace xCategoryService.Interfaces +{ + public interface IXSubCategoryTitleResourceProvider : IXItemResourceProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Models\Dtos\XCategoryDto.cs + +using xModels.Base; + +namespace xCategoryService.Models.Dtos +{ + public class XCategoryDto : XBaseIntIDEntityDto + { + /// + /// Title of SubCategory ... + /// this is a Resource Title ... + /// + /// + public string Title { get; set; } + + /// + /// Description of SubCategory ... + /// this is a Resource Title ... + /// + /// + public string Description { get; set; } + + /// + /// References List String Representation ... + /// each SubCategory Referenced to Category ID which List here ... + /// + /// + public string References { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Models\Dtos\XCategoryResourceDto.cs + +using xModels.Base; +using xModels.Dtos; + +namespace xCategoryService.Models.Dtos +{ + /// + /// Represent a Category by all of it's Resources ... + /// + public class XCategoryResourceDto : XBaseDto + { + /// + /// Category Dto Item for Add or Update ... + /// + public XCategoryDto Item { get; set; } + + /// + /// Exists Category Titles ... + /// + public XResourceDto Titles { get; set; } + + /// + /// Exists Category Description ... + /// + public XResourceDto Descriptions { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Models\Dtos\XSubCategoryDto.cs + +using xModels.Base; + +namespace xCategoryService.Models.Dtos +{ + public class XSubCategoryDto : XBaseIntIDEntityDto + { + /// + /// Title of SubCategory ... + /// this is a Resource Title ... + /// + /// + public string Title { get; set; } + + /// + /// Description of SubCategory ... + /// this is a Resource Title ... + /// + /// + public string Description { get; set; } + + /// + /// References List String Representation ... + /// each SubCategory Referenced to Category ID which List here ... + /// + /// + public string References { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Models\Dtos\XSubCategoryResourceDto.cs + +using xModels.Base; +using xModels.Dtos; + +namespace xCategoryService.Models.Dtos +{ + public class XSubCategoryResourceDto : XBaseDto + { + /// + /// SubCategory Dto Item for Add or Update ... + /// + public XSubCategoryDto Item { get; set; } + + /// + /// Exists SubCategory Titles ... + /// + public XResourceDto Titles { get; set; } + + /// + /// Exists SubCategory Description ... + /// + public XResourceDto Descriptions { get; set; } + + /// + /// Exists Category Resource Dto for Add or Update Category or + /// Related Categopry ... + /// + public XCategoryResourceDto Category { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Models\Entities\XCategory.cs + +using xModels.Base; + +namespace xCategoryService.Models.Entities +{ + public class XCategory : XBaseIntIDEntity + { + /// + /// Title of SubCategory ... + /// this is a Resource Title ... + /// + /// + public string Title { get; set; } + + /// + /// Description of SubCategory ... + /// this is a Resource Title ... + /// + /// + public string Description { get; set; } + + /// + /// References List String Representation ... + /// each SubCategory Referenced to Category ID which List here ... + /// + /// + public string References { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Models\Entities\XSubCategory.cs + +using xModels.Base; + +namespace xCategoryService.Models.Entities +{ + public class XSubCategory : XBaseIntIDEntity + { + /// + /// Title of SubCategory ... + /// this is a Resource Title ... + /// + /// + public string Title { get; set; } + + /// + /// Description of SubCategory ... + /// this is a Resource Title ... + /// + /// + public string Description { get; set; } + + /// + /// References List String Representation ... + /// each SubCategory Referenced to Category ID which List here ... + /// + /// + public string References { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Dtos\XCategoryDtoHub.cs + +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xPushService.Base; + +namespace xCategoryService.Providers.Dtos +{ + public class XCategoryDtoHub : XBaseDtoHub, IXCategoryDtoHub + { + public XCategoryDtoHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Dtos\XCategoryRepositoryService.cs + +using System.Collections.Generic; +using AutoMapper; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xDataService.Providers; + +namespace xCategoryService.Providers.Dtos +{ + public class XCategoryRepositoryService : XBaseRepositoryService, IXCategoryRepositoryService + { + public XCategoryRepositoryService( + IXCategoryRepository repository, + IEnumerable mapperProfiles = null + ) : base( + repository, + mapperProfiles + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Dtos\XCategoryServiceProvider.cs + +using Microsoft.AspNetCore.SignalR; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; + +namespace xCategoryService.Providers.Dtos +{ + public class XCategoryServiceProvider : XBaseDtoProvider, IXCategoryServiceProvider + { + public XCategoryServiceProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXCategoryRepositoryService service, + IXIdentityProvider identityProvider = null + ) : base( + hub, + dataConfiguration, + service, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Dtos\XSubCategoryDtoHub.cs + +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xPushService.Base; + +namespace xCategoryService.Providers.Dtos +{ + public class XSubCategoryDtoHub : XBaseDtoHub, IXSubCategoryDtoHub + { + public XSubCategoryDtoHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Dtos\XSubCategoryRepositoryService.cs + +using System.Collections.Generic; +using AutoMapper; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xDataService.Providers; + +namespace xCategoryService.Providers.Dtos +{ + public class XSubCategoryRepositoryService : XBaseRepositoryService, IXSubCategoryRepositoryService + { + public XSubCategoryRepositoryService( + IXSubCategoryRepository repository, + IEnumerable mapperProfiles = null + ) : base( + repository, + mapperProfiles + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Dtos\XSubCategoryServiceProvider.cs + +using Microsoft.AspNetCore.SignalR; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Models.Dtos; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; + +namespace xCategoryService.Providers.Dtos +{ + public class XSubCategoryServiceProvider : XBaseDtoProvider, IXSubCategoryServiceProvider + { + public XSubCategoryServiceProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXSubCategoryRepositoryService service, + IXIdentityProvider identityProvider = null + ) : base( + hub, + dataConfiguration, + service, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XCategoryEFRepository.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public class XCategoryEFRepository : XBaseEFRepository, IXCategoryRepository + where TContext : XDbContext + { + public XCategoryEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXCategoryKeyGenerator keyGenerator = null, + IXCategoryRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XCategoryEntityHub.cs + +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xPushService.Base; + +namespace xCategoryService.Providers.Entities +{ + public class XCategoryEntityHub : XBaseEntityHub, IXCategoryEntityHub + { + public XCategoryEntityHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XCategoryInMemoryRepository.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public class XCategoryInMemoryRepository : XBaseInMemoryRepository, IXCategoryRepository + { + public XCategoryInMemoryRepository( + XDataServiceConfiguration configuration, + IXCategoryKeyGenerator keyGenerator = null, + IXCategoryRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XCategoryKeyGenerator.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public class XCategoryKeyGenerator : XIntKeyGenerator, IXCategoryKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XCategoryMongoRepository.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public class XCategoryMongoRepository : XBaseMongoRepository, IXCategoryRepository + { + public XCategoryMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXCategoryKeyGenerator keyGenerator = null, + IXCategoryRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XCategoryRepositoryEvents.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public class XCategoryRepositoryEvents : XBaseRepositoryEvents, IXCategoryRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XCategoryRepositoryProvider.cs + +using Microsoft.AspNetCore.SignalR; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; + +namespace xCategoryService.Providers.Entities +{ + public class XCategoryRepositoryProvider : XBaseEntityProvider, IXCategoryRepositoryProvider + { + public XCategoryRepositoryProvider( + IHubContext hub, + IXCategoryRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + repository, + dataConfiguration, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XCategorySeederBase.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public abstract class XCategorySeederBase : XBaseDbSeeder, IXCategorySeeder + { + protected XCategorySeederBase( + string entity, + string database, + IXCategoryRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + entity, + database, + repository, + dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XSubCategoryEFRepository.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public class XSubCategoryEFRepository : XBaseEFRepository, IXSubCategoryRepository + where TContext : XDbContext + { + public XSubCategoryEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXSubCategoryKeyGenerator keyGenerator = null, + IXSubCategoryRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XSubCategoryEntityHub.cs + +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xPushService.Base; + +namespace xCategoryService.Providers.Entities +{ + public class XSubCategoryEntityHub : XBaseEntityHub, IXSubCategoryEntityHub + { + public XSubCategoryEntityHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XSubCategoryInMemoryRepository.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public class XSubCategoryInMemoryRepository : XBaseInMemoryRepository, IXSubCategoryRepository + { + public XSubCategoryInMemoryRepository( + XDataServiceConfiguration configuration, + IXSubCategoryKeyGenerator keyGenerator = null, + IXSubCategoryRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XSubCategoryKeyGenerator.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public class XSubCategoryKeyGenerator : XIntKeyGenerator, IXSubCategoryKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XSubCategoryMongoRepository.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public class XSubCategoryMongoRepository : XBaseMongoRepository, IXSubCategoryRepository + { + public XSubCategoryMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXSubCategoryKeyGenerator keyGenerator = null, + IXSubCategoryRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XSubCategoryRepositoryEvents.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public class XSubCategoryRepositoryEvents : XBaseRepositoryEvents, IXSubCategoryRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XSubCategoryRepositoryProvider.cs + +using Microsoft.AspNetCore.SignalR; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; + +namespace xCategoryService.Providers.Entities +{ + public class XSubCategoryRepositoryProvider : XBaseEntityProvider, IXSubCategoryRepositoryProvider + { + public XSubCategoryRepositoryProvider( + IHubContext hub, + IXSubCategoryRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + repository, + dataConfiguration, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\Entities\XSubCategorySeederBase.cs + +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.Providers; + +namespace xCategoryService.Providers.Entities +{ + public abstract class XSubCategorySeederBase : XBaseDbSeeder, IXSubCategorySeeder + { + protected XSubCategorySeederBase( + string entity, + string database, + IXSubCategoryRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + entity, + database, + repository, + dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\GraphQL\XCategoryGraphQLTypeHelper.cs + +using xCategoryService.Constants; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.GraphQL; + +namespace xCategoryService.Providers.GraphQL +{ + public class XCategoryGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXCategoryGraphQLTypeHelper + { + public XCategoryGraphQLTypeHelper( + XDataServiceConfiguration configuration + ) : base(configuration) + { } + + public override string GetInQueryCollectionName() + { + return XCategoryServiceConstants.XCategoryCollectionName; + } + + public override string GetInQuerySingleName() + { + return XCategoryServiceConstants.XCategorySingleName; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\GraphQL\XCategoryGraphQuery.cs + +using GraphQL.Types; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.GraphQL; + +namespace xCategoryService.Providers.GraphQL +{ + public class XCategoryGraphQuery : XBaseGraphQLQuery + { + public XCategoryGraphQuery( + XDataServiceConfiguration configuration, + IXCategoryRepository repository, + IXCategoryGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\GraphQL\XCategoryGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xCategoryService.Providers.GraphQL +{ + public class XCategoryGraphSchema : Schema + { + public XCategoryGraphSchema(IServiceProvider services) : base(services) + { + Query = (XCategoryGraphQuery)services.GetService(typeof(XCategoryGraphQuery)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\GraphQL\XCategoryGraphType.cs + +using xCategoryService.Models.Entities; +using xDataService.GraphQL; + +namespace xCategoryService.Providers.GraphQL +{ + public class XCategoryGraphType : XBaseGraphObjectType + { + public XCategoryGraphType() : base() + { + Field(x => x.Title); + Field(x => x.Description); + Field(x => x.References); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\GraphQL\XSubCategoryGraphQLTypeHelper.cs + +using xCategoryService.Constants; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.GraphQL; + +namespace xCategoryService.Providers.GraphQL +{ + public class XSubCategoryGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXSubCategoryGraphQLTypeHelper + { + public XSubCategoryGraphQLTypeHelper( + XDataServiceConfiguration configuration + ) : base(configuration) + { } + + public override string GetInQueryCollectionName() + { + return XCategoryServiceConstants.XSubCategoryCollectionName; + } + + public override string GetInQuerySingleName() + { + return XCategoryServiceConstants.XSubCategorySingleName; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\GraphQL\XSubCategoryGraphQuery.cs + +using GraphQL.Types; +using xCategoryService.Interfaces.Entities; +using xCategoryService.Models.Entities; +using xDataService.Configuration; +using xDataService.GraphQL; + +namespace xCategoryService.Providers.GraphQL +{ + public class XSubCategoryGraphQuery : XBaseGraphQLQuery + { + public XSubCategoryGraphQuery( + XDataServiceConfiguration configuration, + IXSubCategoryRepository repository, + IXSubCategoryGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\GraphQL\XSubCategoryGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xCategoryService.Providers.GraphQL +{ + public class XSubCategoryGraphSchema : Schema + { + public XSubCategoryGraphSchema(IServiceProvider services) : base(services) + { + Query = (XSubCategoryGraphQuery)services.GetService(typeof(XSubCategoryGraphQuery)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\GraphQL\XSubCategoryGraphType.cs + +using xCategoryService.Models.Entities; +using xDataService.GraphQL; + +namespace xCategoryService.Providers.GraphQL +{ + public class XSubCategoryGraphType : XBaseGraphObjectType + { + public XSubCategoryGraphType() : base() + { + Field(x => x.Title); + Field(x => x.Description); + Field(x => x.References); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XBaseCategoryProvider.cs + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using xCategoryService.Extensions; +using xCategoryService.Interfaces; +using xCategoryService.Interfaces.Dtos; +using xCategoryService.Models.Dtos; +using xCommons.Extensions; +using xDataService.Extensions; +using xDataService.Models; +using xExceptions.Constants; +using xModels.Dtos; + +namespace xCategoryService.Providers +{ + public abstract class XBaseCategoryProvider : IXBaseCategoryProvider + { + // + #region Props ... + /// + /// Provider Identifier ... + /// + public string Provider { get; } + + /// + /// Tag Provider for Maipulating Tags ... + /// + public IXCategoryServiceProvider CategoryProvider { get; } + #endregion + + // + #region Constructor ... + public XBaseCategoryProvider( + string providedFor, + IXCategoryServiceProvider categoryProvider + ) + { + // + Provider = providedFor; + CategoryProvider = categoryProvider; + } + #endregion + + // + #region Referenced Actions ... + /// + /// Get Specified Indexed Reference for Specific Provided ID ... + /// + /// + /// + /// + /// + public async Task GetReference( + TKey providedForId, + int forIndex = 0, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + if (forIndex < 0) + { + forIndex = 0; + } + + // + // Retrieve Last Index ... + var indexes = await GetReferencingIndexes( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + var maxIndex = indexes.HasChild() ? indexes.Max() : -1; + if (forIndex > maxIndex) + { + XException.NotFound.Throw(); + } + + // + var indexedIdentifier = GetIndexedProvidedID( + forIndex: forIndex, + providedForId: providedForId + ); + var result = await CategoryProvider.FindOneAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: x => x.References.Contains(indexedIdentifier) + ); + if (result.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + return result; + } + + /// + /// Retrieve all Exists References of Specific Provided ID ... + /// + /// + /// + /// + public async Task> GetAllReferences( + TKey providedForId, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + var identifier = GetProvidedID(providedForId); + var result = await CategoryProvider.FindManyAsync( + orderBuilder: null, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: x => x.References.Contains(identifier) + ); + + // + return result; + } + + /// + /// Extract all Referencing Models for Specified Key ... + /// + /// + /// + /// + public async Task>> GetAllReferencings( + TKey providedForId, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !providedForId.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var identifier = GetProvidedID(providedForId); + + // + var result = (await CategoryProvider.FindManyAsync( + orderBuilder: null, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: x => x.References.Contains(identifier) + )) + .Select(x => x.References) + .ToList() + .SelectMany(x => x.ParseXReferenceList()) + .Where(x => x.ProvidedFor == Provider && $"{x.ReferencedTo}" == $"{providedForId}") + .OrderBy(x => x.Index) + .AsEnumerable(); + + // + return result; + } + + /// + /// Count References ... + /// + /// + /// + /// + public async Task CountReferences( + TKey providedForId, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = !providedForId.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var identifier = GetProvidedID(providedForId); + var result = (await CategoryProvider.FindManyAsync( + orderBuilder: null, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: x => x.References.Contains(identifier) + )) + .Count(); + + // + return result; + } + + /// + /// Retrieve References of Specific Provided ID as Query ... + /// + /// + /// + /// + /// + public async Task> QueryReferences( + XQuery query, + TKey providedForId, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (query.IsNull() || providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + var identifier = GetProvidedID(providedForId); + var result = await CategoryProvider.QueryAsync( + query: query, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + orderBuilder: x => x.OrderBy(y => y.Id), + predicate: x => x.References.Contains(identifier) + ); + + // + return result; + } + + /// + /// Add Reference a Model to Specific Provided ID ... + /// + /// + /// + /// + /// + /// + public async Task AddReference( + XCategoryDto model, + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = false; + + // + // Validate ... + result = !model.IsNullOrDefault() && + model.Id.IsValidIntId() && + !providedForId.IsNull(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Model Exists ... + result = await CategoryProvider.IsExistsAsync(model.Id); + if (!result) + { + XException.NotFound.Throw(); + } + + // + // Update Model by Retrieving ... + model = await CategoryProvider.GetAsync( + id: model.Id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + + // + // Retrieve all References ... + var references = await GetAllReferences( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + // Check isReferenced or not ... + result = references + .Any(r => + r.Id == model.Id && + r.GetReferences() + .Any(rf => + rf.ProvidedFor == Provider && + rf.ReferencedTo == $"{providedForId}")); + if (result) + { + return result; + } + + // + // Here means there is not any reference to ProvidedForId ... + int forIndex = await CountReferences( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + var modelReferences = model.GetReferences(); + modelReferences.Add(new XReference + { + Index = forIndex, + ProvidedFor = Provider, + ReferencedTo = $"{providedForId}" + }); + modelReferences = modelReferences + .OrderBy(r => r.Index) + .ToList(); + model = model.UpdateReferences(modelReferences); + + // + // Update Data Base ... + model = await CategoryProvider.UpdateAsync( + id: model.Id, + item: model, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + result = !model.IsNullOrDefault(); + + // + return result; + } + + /// + /// Add Reference a Model to Specific XRefence ... + /// + /// + /// + /// + /// + /// + public async Task AddReference( + XCategoryDto model, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + // Since other Validations Handled in Calling, we Ignore them here ... + var result = + !model.IsNull() && + !reference.IsNullOrDefault() && + reference.ProvidedFor == Provider && + !reference.ReferencedTo.IsNull(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await AddReference( + model: model, + connectionId: connectionId, + cancellationToken: cancellationToken, + providedForId: reference.ReferencedTo + ); + + // + return result; + } + + /// + /// Remove Reference a Model for Specific Provided ID ... + /// + /// + /// + /// + /// + /// + public async Task RemoveReference( + XCategoryDto model, + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = + !providedForId.IsNull() && + !model.IsNullOrDefault() && + model.Id.IsValidIntId(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Model ... + model = await CategoryProvider.GetAsync( + id: model.Id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + result = !model.IsNullOrDefault(); + if (!result) + { + XException.NotFound.Throw(); + } + + // + // Reading Referencing Indexes ... + var indexes = await GetReferencingIndexes( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + // Check Model Reference to ID ... + var referencesCount = await CountReferences( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + 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 + .GetReferences() + .FirstOrDefault(r => + r.ProvidedFor == Provider && + r.ReferencedTo == $"{providedForId}"); + result = !reference.IsNullOrDefault(); + if (!result) + { + XException.ActionFailed.Throw(); + } + + // + // Remove Referenced Item from Model ... + var modelReferences = model + .GetReferences() + .Where(r => + !(r.ProvidedFor == Provider && + r.ReferencedTo == $"{providedForId}")) + .ToList(); + model = model.UpdateReferences(modelReferences); + + // + // Update DataBase ... + model = await CategoryProvider.UpdateAsync( + item: model, + id: model.Id, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + 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, + cancellationToken: cancellationToken + ); + if (!dto.IsNullOrDefault()) + { + // + var dtoReferences = dto.GetReferences(); + dtoReferences + .ToList() + .ForEach(iref => + { + // + if ( + iref.Index == i && + iref.ProvidedFor == Provider && + iref.ReferencedTo == reference.ReferencedTo + ) + { + iref.Index = i - 1; + } + }); + dto = dto.UpdateReferences(dtoReferences); + + // + dto = await CategoryProvider.UpdateAsync( + item: dto, + id: dto.Id, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + } + } + + // + return result; + } + + /// + /// Remove Reference a Model for Specific XRefence ... + /// + /// + /// + /// + /// + /// + public async Task RemoveReference( + XCategoryDto model, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + if (model.IsNullOrDefault() || + reference.IsNullOrDefault() || + reference.ReferencedTo.IsNull() || + reference.ProvidedFor != Provider) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await RemoveReference( + model: model, + connectionId: connectionId, + cancellationToken: cancellationToken, + providedForId: reference.ReferencedTo + ); + + // + return result; + } + + /// + /// Remove All References to Specified Key ... + /// + /// + /// + /// + /// + public async Task RemoveReferences( + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = false; + var references = await GetAllReferences( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + if (!references.IsNull() && references.HasChild()) + { + // + foreach (var reference in references) + { + // + // Remove Reference ... + var itemReferences = reference.GetReferences() + .Where(r => + r.ProvidedFor != Provider || + (r.ProvidedFor == Provider && + r.ReferencedTo != $"{providedForId}")) + .ToList(); + var updatedModel = reference.UpdateReferences(itemReferences); + updatedModel = await CategoryProvider.UpdateAsync( + saveChanges: true, + item: updatedModel, + id: updatedModel.Id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + 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, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !providedForId.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = new List(); + var referencings = await GetAllReferencings( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + isValid = + !referencings.IsNull() && + referencings.HasChild(); + if (!isValid) + { + return result; + } + + // + result = + referencings + .OrderBy(r => r.Index) + .Select(r => r.Index) + .ToList(); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XCategoryDescriptionResourceProvider.cs + +using xCategoryService.Constants; +using xCategoryService.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Providers; + +namespace xCategoryService.Providers +{ + public class XCategoryDescriptionResourceProvider : XBaseItemResourceProvider, IXCategoryDescriptionResourceProvider + { + public XCategoryDescriptionResourceProvider( + IXStringServiceProvider stringProvider + ) : base( + stringProvider: stringProvider, + item: XCategoryServiceConstants.CategoryDescriptionResourceIdentifier + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XCategoryEntityRegisterar.cs + +using Microsoft.EntityFrameworkCore; +using xCategoryService.Configurations.Entities; +using xCategoryService.Models.Entities; +using xDataService.Interfaces; +using xDataService.Providers; + +namespace xCategoryService.Providers +{ + public class XCategoryEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar + { + public XCategoryEntityRegisterar() + : base(nameof(XCategoryEntityRegisterar)) + { + // + // Add XCategory Entity ... + AddEntity(); + } + + /// + /// Configure Entities ... + /// + /// + public override void ConfigureEntities(ModelBuilder modelBuilder) + { + // + // Configure Entity Using Provided ... + modelBuilder + .ApplyConfigurationsFromAssembly(typeof(XCategoryEntityConfiguration).Assembly); + + // + base.ConfigureEntities(modelBuilder); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XCategoryProvider.cs + +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; +using xDataService.Models; + +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; + private readonly IXCategorySubCategoryReferenceProvider categorySubCategoryReferenceProvider; + + public string Provider => categorySubCategoryReferenceProvider.Provider; + + public XCategoryProvider( + XAppConfiguration appConfiguration, + IXCategoryServiceProvider provider, + XDataServiceConfiguration dataServiceConfiguration, + IXCategoryTitleResourceProvider titleResourceProvider, + IXCategoryDescriptionResourceProvider descriptionResourceProvider, + IXCategorySubCategoryReferenceProvider categorySubCategoryReferenceProvider + ) : base( + provider, + permittedRoles: new string[] { } + ) + { + // + this.provider = provider; + this.appConfiguration = appConfiguration; + this.titleResourceProvider = titleResourceProvider; + this.dataServiceConfiguration = dataServiceConfiguration; + this.descriptionResourceProvider = descriptionResourceProvider; + this.categorySubCategoryReferenceProvider = categorySubCategoryReferenceProvider; + } + + // + #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; + } + + /// + /// Add Category by It's Locales ... + /// + /// + /// + /// + /// + /// + public async Task AddResource( + XCategoryResourceDto item, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !item.IsNullOrDefault() && + !item.Titles.IsNullOrDefault() && + item.Titles.Locales.HasChild() && + !item.Titles.Locales.Any(il => + il.Language.IsNullOrEmpty() || + il.Value.IsNullOrEmpty()); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + if (item.Item.IsNullOrDefault()) + { + item.Item = new XCategoryDto(); + } + + // + // Check Permission ... + isValid = HasPermision(userInfo); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + var id = -1; + try + { + // + // Add Category for ID Generation ... + item.Item = await base.Add( + item: item.Item, + userInfo: userInfo, + connectionId: null, // Since here We dont want notification ... + cancellationToken: cancellationToken + ); + isValid = !item.Item.IsNullOrDefault(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + id = item.Item.Id; + + // + foreach (var il in item.Titles.Locales) + { + // + var title = il.Value; + var language = il.Language; + + // + var descriptionItem = + item.Descriptions.IsNullOrDefault() || + !item.Descriptions.Locales.HasChild() + ? null + : item.Descriptions.Locales + .FirstOrDefault(ides => + ides.Language + .Equals( + language, + StringComparison.InvariantCultureIgnoreCase)); + var description = + descriptionItem.IsNullOrDefault() + ? string.Empty + : descriptionItem.Value; + + // + // Try to Add Resources ... + 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 if not ... + if (item.Item.Title.IsNullOrEmpty() || + !item.Item.Title + .Equals( + titleResource.Resource, + StringComparison.InvariantCultureIgnoreCase) + ) + { + // + item.Item.Title = titleResource.Resource; + item.Item.Description = descriptionResource.Resource; + item.Item = await base.Update( + id: id, + item: 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 ... + result = await GetResource( + id: id, + cancellationToken: cancellationToken + ); + + // + #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.Value + .Equals(l.Value, StringComparison.InvariantCultureIgnoreCase) && + r.Language + .Equals(l.Language, 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.Value + .Equals(l.Value, StringComparison.InvariantCultureIgnoreCase) && + r.Language + .Equals(l.Language, 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, 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, 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 + + // + #region SubCategory References ... + /// + /// Get Specified Indexed Reference for Specific Provided ID ... + /// + /// + /// + /// + /// + public async Task GetReference( + int providedForId, + int forIndex = 0, + CancellationToken cancellationToken = default + ) + { + // + var result = await categorySubCategoryReferenceProvider + .GetReference( + forIndex: forIndex, + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Retrieve all Exists References of Specific Provided ID ... + /// + /// + /// + /// + public async Task> GetAllReferences( + int providedForId, + CancellationToken cancellationToken = default + ) + { + // + var result = await categorySubCategoryReferenceProvider + .GetAllReferences( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Extract all Referencing Models for Specified Key ... + /// + /// + /// + /// + public async Task>> GetAllReferencings( + int providedForId, + CancellationToken cancellationToken = default + ) + { + // + var result = await categorySubCategoryReferenceProvider + .GetAllReferencings( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Count References ... + /// + /// + /// + /// + public async Task CountReferences( + int providedForId, + CancellationToken cancellationToken = default + ) + { + // + var result = await categorySubCategoryReferenceProvider + .CountReferences( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Retrieve References of Specific Provided ID as Query ... + /// + /// + /// + /// + /// + public async Task> QueryReferences( + XQuery query, + int providedForId, + CancellationToken cancellationToken = default + ) + { + // + var result = await categorySubCategoryReferenceProvider + .QueryReferences( + query: query, + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Add Reference a Model to Specific Provided ID ... + /// + /// + /// + /// + /// + /// + public async Task AddReference( + XCategoryDto model, + int providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await categorySubCategoryReferenceProvider + .AddReference( + model: model, + connectionId: connectionId, + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Add Reference a Model to Specific XRefence ... + /// + /// + /// + /// + /// + /// + public async Task AddReference( + XCategoryDto model, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await categorySubCategoryReferenceProvider + .AddReference( + model: model, + reference: reference, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Remove Reference a Model for Specific Provided ID ... + /// + /// + /// + /// + /// + /// + public async Task RemoveReference( + XCategoryDto model, + int providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await categorySubCategoryReferenceProvider + .RemoveReference( + model: model, + connectionId: connectionId, + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Remove Reference a Model for Specific XRefence ... + /// + /// + /// + /// + /// + /// + public async Task RemoveReference( + XCategoryDto model, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await categorySubCategoryReferenceProvider + .RemoveReference( + model: model, + reference: reference, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Remove All References to Specified Key ... + /// + /// + /// + /// + /// + public async Task RemoveReferences( + int providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await categorySubCategoryReferenceProvider + .RemoveReferences( + connectionId: connectionId, + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XCategoryService.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using xCategoryService.Interfaces; +using xCategoryService.Models.Dtos; +using xIdentityModels.Models; +using xModels.Dtos; + +namespace xCategoryService.Providers +{ + /// + /// Completely Exported Service ... + /// + public class XCategoryService : IXCategoryService + { + private readonly IXCategoryProvider categoryProvider; + private readonly IXSubCategoryProvider subCategoryProvider; + + public XCategoryService( + IXCategoryProvider categoryProvider, + IXSubCategoryProvider subCategoryProvider + ) + { + this.categoryProvider = categoryProvider; + this.subCategoryProvider = subCategoryProvider; + } + + // + #region Category Provider ... + /// + /// Check Has Specified Locale or not ... + /// + /// + /// + /// + /// + public virtual async Task CategoryHasLocale( + int id, + string language, + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .HasLocale( + id: id, + language: language, + cancellationToken: cancellationToken + ); + } + + /// + /// Load an Item by all of it's Related Translations ... + /// + /// + /// + /// + public virtual async Task GetCategoryResource( + int id, + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .GetResource( + id: id, + cancellationToken: cancellationToken + ); + } + + /// + /// Add Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task AddCategory( + XCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .Add( + item: item, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Add Item by It's Locales ... + /// + /// + /// + /// + /// + /// + public virtual async Task AddCategoryResource( + XCategoryResourceDto item, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .AddResource( + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Remove Item for Specified Language ... + /// + /// + /// if null, Remove All + /// + /// + /// + /// + public virtual async Task RemoveCategory( + int id, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .Remove( + id: id, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Update an Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + /// + public virtual async Task UpdateCategory( + int id, + XCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .Update( + id: id, + item: item, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Refresh a Resource ... + /// + /// + /// + /// + /// + /// + public virtual async Task RefreshCategory( + XCategoryResourceDto resource, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .Refresh( + resource: resource, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Get All Resources ... + /// + /// + /// + public virtual async Task> GetAllCategoryResources( + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .GetAllResources( + cancellationToken: cancellationToken + ); + } + + /// + /// Find One Resource ... + /// + /// + /// + /// + public virtual async Task FindOneCategoryResource( + Expression> predicate = null, + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .FindOneResource( + predicate: predicate, + cancellationToken: cancellationToken + ); + } + + /// + /// Find Many Resource ... + /// + /// + /// + /// + /// + public virtual async Task> FindManyCategoryResource( + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .FindManyResource( + predicate: predicate, + orderBuilder: orderBuilder, + cancellationToken: cancellationToken + ); + } + + /// + /// Retrieve Resources by Query Pattern ... + /// + /// + /// + /// + /// + /// + public virtual async Task> QueryCategoryResources( + XQuery query = null, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + return await categoryProvider + .QueryResources( + query: query, + predicate: predicate, + orderBuilder: orderBuilder, + cancellationToken: cancellationToken + ); + } + + /// + /// Get All Resources as Async Enumerable ... + /// + /// + /// + public virtual IAsyncEnumerable GetAllCategoryResourcesAsAsyncEnumerable( + CancellationToken cancellationToken = default + ) + { + // + return categoryProvider + .GetAllResourcesAsAsyncEnumerable( + cancellationToken: cancellationToken + ); + } + #endregion + + // + #region SubCategory Provider ... + /// + /// Check Has Specified Locale or not ... + /// + /// + /// + /// + /// + public virtual async Task SubCategoryHasLocale( + int id, + string language, + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .HasLocale( + id: id, + language: language, + cancellationToken: cancellationToken + ); + } + + /// + /// Load an Item by all of it's Related Translations ... + /// + /// + /// + /// + public virtual async Task GetSubCategoryResource( + int id, + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .GetResource( + id: id, + cancellationToken: cancellationToken + ); + } + + /// + /// Add Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task AddSubCategory( + XSubCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .Add( + item: item, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Add Item by it's Locales and Reference ... + /// + /// + /// + /// + /// + /// + public virtual async Task AddSubCategoryResource( + XSubCategoryResourceDto item, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .AddResource( + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Remove Item for Specified Language ... + /// + /// + /// if null, Remove All + /// + /// + /// + /// + public virtual async Task RemoveSubCategory( + int id, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .Remove( + id: id, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Update an Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + /// + public virtual async Task UpdateSubCategory( + int id, + XSubCategoryDto item, + string language = null, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .Update( + id: id, + item: item, + language: language, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Refresh a Resource ... + /// + /// + /// + /// + /// + /// + public virtual async Task RefreshSubCategory( + XSubCategoryResourceDto resource, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .Refresh( + resource: resource, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Get All Resources ... + /// + /// + /// + public virtual async Task> GetAllSubCategoryResources( + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .GetAllResources( + cancellationToken: cancellationToken + ); + } + + /// + /// Find One Resource ... + /// + /// + /// + /// + public virtual async Task FindOneSubCategoryResource( + Expression> predicate = null, + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .FindOneResource( + predicate: predicate, + cancellationToken: cancellationToken + ); + } + + /// + /// Find Many Resource ... + /// + /// + /// + /// + /// + public virtual async Task> FindManySubCategoryResource( + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .FindManyResource( + predicate: predicate, + orderBuilder: orderBuilder, + cancellationToken: cancellationToken + ); + } + + /// + /// Retrieve Resources by Query Pattern ... + /// + /// + /// + /// + /// + /// + public virtual async Task> QuerySubCategoryResources( + XQuery query = null, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + return await subCategoryProvider + .QueryResources( + query: query, + predicate: predicate, + orderBuilder: orderBuilder, + cancellationToken: cancellationToken + ); + } + + /// + /// Get All Resources as Async Enumerable ... + /// + /// + /// + public virtual IAsyncEnumerable GetAllSubCategoryResourcesAsAsyncEnumerable( + CancellationToken cancellationToken = default + ) + { + // + return subCategoryProvider + .GetAllResourcesAsAsyncEnumerable( + cancellationToken: cancellationToken + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XCategorySubCategoryReferenceProvider.cs + +using xCategoryService.Constants; +using xCategoryService.Interfaces; +using xCategoryService.Interfaces.Dtos; + +namespace xCategoryService.Providers +{ + public class XCategorySubCategoryReferenceProvider : XBaseCategoryProvider, IXCategorySubCategoryReferenceProvider + { + public XCategorySubCategoryReferenceProvider( + IXCategoryServiceProvider categoryProvider + ) : base( + categoryProvider: categoryProvider, + providedFor: XCategoryServiceConstants.SubCategoryReference + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XCategoryTitleResourceProvider.cs + +using xCategoryService.Constants; +using xCategoryService.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Providers; + +namespace xCategoryService.Providers +{ + public class XCategoryTitleResourceProvider : XBaseItemResourceProvider, IXCategoryTitleResourceProvider + { + public XCategoryTitleResourceProvider( + IXStringServiceProvider stringProvider + ) : base( + stringProvider: stringProvider, + item: XCategoryServiceConstants.CategoryTitleResourceIdentifier + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XSubCategoryDescriptionResourceProvider.cs + +using xCategoryService.Constants; +using xCategoryService.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Providers; + +namespace xCategoryService.Providers +{ + public class XSubCategoryDescriptionResourceProvider : XBaseItemResourceProvider, IXSubCategoryDescriptionResourceProvider + { + public XSubCategoryDescriptionResourceProvider( + IXStringServiceProvider stringProvider + ) : base( + stringProvider: stringProvider, + item: XCategoryServiceConstants.SubCategoryDescriptionResourceIdentifier + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XSubCategoryEntityRegisterar.cs + +using Microsoft.EntityFrameworkCore; +using xCategoryService.Configurations.Entities; +using xCategoryService.Models.Entities; +using xDataService.Interfaces; +using xDataService.Providers; + +namespace xCategoryService.Providers +{ + public class XSubCategoryEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar + { + public XSubCategoryEntityRegisterar() + : base(nameof(XSubCategoryEntityRegisterar)) + { + // + // Add Entity ... + AddEntity(); + } + + /// + /// Configure Entities ... + /// + /// + public override void ConfigureEntities(ModelBuilder modelBuilder) + { + // + // Configure Entity Using Provided ... + modelBuilder + .ApplyConfigurationsFromAssembly(typeof(XSubCategoryEntityConfiguration).Assembly); + + // + base.ConfigureEntities(modelBuilder); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XSubCategoryProvider.cs + +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 xDataService.Configuration; +using xExceptions.Constants; +using xIdentityModels.Models; +using xModels.Dtos; +using xDataService.Extensions; +using xPushService.Constants; +using xPushService.Providers; + +namespace xCategoryService.Providers +{ + public class XSubCategoryProvider : XBaseProvided, IXSubCategoryProvider + { + private readonly XAppConfiguration appConfiguration; + private readonly IXCategoryProvider categoryProvider; + private readonly IXSubCategoryServiceProvider provider; + private readonly XDataServiceConfiguration dataServiceConfiguration; + private readonly IXSubCategoryTitleResourceProvider titleResourceProvider; + private readonly IXSubCategoryDescriptionResourceProvider descriptionResourceProvider; + + public XSubCategoryProvider( + XAppConfiguration appConfiguration, + IXCategoryProvider categoryProvider, + IXSubCategoryServiceProvider provider, + XDataServiceConfiguration dataServiceConfiguration, + IXSubCategoryTitleResourceProvider titleResourceProvider, + IXSubCategoryDescriptionResourceProvider descriptionResourceProvider + ) : base( + provider, + permittedRoles: new string[] { } + ) + { + this.provider = provider; + this.appConfiguration = appConfiguration; + this.categoryProvider = categoryProvider; + this.titleResourceProvider = titleResourceProvider; + this.dataServiceConfiguration = dataServiceConfiguration; + this.descriptionResourceProvider = descriptionResourceProvider; + } + + // + #region Overrides ... + public override bool IsOwned( + XSubCategoryDto item, + XUserClaimsInfoDto userInfo + ) + { + return !item.IsNullOrDefault(); + } + + public override bool IsOwned( + XSubCategory 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 + ); + + // + // Reading Category Resource ... + XCategoryResourceDto categoryResourceDto = null; + try + { + var categoryDto = await categoryProvider.GetReference( + providedForId: id, + cancellationToken: cancellationToken + ); + if (!categoryDto.IsNullOrDefault()) + { + categoryResourceDto = await categoryProvider.GetResource( + id: categoryDto.Id, + cancellationToken: cancellationToken + ); + } + } + catch { } + + // + var result = new XSubCategoryResourceDto + { + Item = item, + Titles = titleResource, + Category = categoryResourceDto, + Descriptions = descriptionResource + }; + + // + return result; + } + + /// + /// Add an Item for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + public async Task Add( + XSubCategoryDto 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; + } + + /// + /// Add Item by it's Locales and Reference ... + /// + /// + /// + /// + /// + /// + public async Task AddResource( + XSubCategoryResourceDto item, + string connectionId = null, + XUserClaimsInfoDto userInfo = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !item.IsNullOrDefault() && + !item.Titles.IsNullOrDefault() && + item.Titles.Locales.HasChild() && + !item.Titles.Locales.Any(tl => + tl.Value.IsNullOrEmpty() || + tl.Language.IsNullOrEmpty()); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + if (item.Item.IsNullOrDefault()) + { + item.Item = new XSubCategoryDto(); + } + + // + // Check Permission ... + isValid = HasPermision(userInfo); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + var id = -1; + try + { + // + // Handle Category Generation ... + if (!item.Category.IsNullOrDefault()) + { + // + // Handle Category ... + try + { + // Try to Add Category ... + item.Category = await categoryProvider.Refresh( + resource: item.Category, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + catch { } + } + + // + // Add SubCategory for ID Generation ... + item.Item = await base.Add( + item: item.Item, + userInfo: userInfo, + connectionId: null, // Since here We dont want notification ... + cancellationToken: cancellationToken + ); + isValid = !item.Item.IsNullOrDefault(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + id = item.Item.Id; + + // + foreach (var il in item.Titles.Locales) + { + // + var title = il.Value; + var language = il.Language; + + // + var descriptionItem = + item.Descriptions.IsNullOrDefault() || + !item.Descriptions.Locales.HasChild() + ? null + : item.Descriptions.Locales + .FirstOrDefault(ides => + ides.Language + .Equals( + language, + StringComparison.InvariantCultureIgnoreCase)); + var description = + descriptionItem.IsNullOrDefault() + ? string.Empty + : descriptionItem.Value; + + // + // Try to Add Resources ... + 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 if not ... + if (item.Item.Title.IsNullOrEmpty() || + !item.Item.Title + .Equals( + titleResource.Resource, + StringComparison.InvariantCultureIgnoreCase) + ) + { + // + item.Item.Title = titleResource.Resource; + item.Item.Description = descriptionResource.Resource; + item.Item = await base.Update( + id: id, + item: item.Item, + userInfo: userInfo, + connectionId: null, + cancellationToken: cancellationToken + ); + } + } + } + catch + { + XException.InvalidArgs.Throw(); + } + + // + // Validate Id ... + isValid = id.IsValidIntId(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + + // + // Add Resource Reference to Category ... + isValid = await categoryProvider.AddReference( + providedForId: id, + connectionId: null, + model: item.Category.Item, + cancellationToken: cancellationToken + ); + + // + // 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; + } + + // + // Remove all Referencings ... + if (isValid) + { + await categoryProvider.RemoveReferences( + providedForId: id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + 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, + XSubCategoryDto 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( + XSubCategoryResourceDto 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; + XSubCategoryResourceDto result = null; + + // + // Check Item Exists ... + XSubCategoryDto 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 XSubCategoryDto + { + 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 ... + result = await GetResource( + id: id, + cancellationToken: cancellationToken + ); + + // + #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.Value + .Equals(l.Value, StringComparison.InvariantCultureIgnoreCase) && + r.Language + .Equals(l.Language, 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.Value + .Equals(l.Value, StringComparison.InvariantCultureIgnoreCase) && + r.Language + .Equals(l.Language, 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, 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, StringComparison.InvariantCultureIgnoreCase)) + ); + foreach (var locale in newDescriptions) + { + // + await descriptionResourceProvider.AddOrUpdateLocale( + identifier: id, + locale: locale, + connectionId: null, + cancellationToken: cancellationToken + ); + } + #endregion + } + } + + // + // Handle Category ... + if ( + !isDelete && + (isNew || isExists) && + !resource.Category.IsNullOrDefault()) + { + // + // Refresh Category ... + var category = await categoryProvider.Refresh( + userInfo: userInfo, + resource: resource.Category, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + // Handle SubCategory Referencings ... + var references = await categoryProvider.GetAllReferences( + providedForId: id, + cancellationToken: cancellationToken + ); + var hasReference = + references.HasChild() && + references.Any(r => r.Id == category.Item.Id); + if (!hasReference) + { + await categoryProvider.AddReference( + providedForId: id, + model: category.Item, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + } + + // + // 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; + XSubCategoryResourceDto 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 + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCategoryService\Providers\XSubCategoryTitleResourceProvider.cs + +using xCategoryService.Constants; +using xCategoryService.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Providers; + +namespace xCategoryService.Providers +{ + public class XSubCategoryTitleResourceProvider : XBaseItemResourceProvider, IXSubCategoryTitleResourceProvider + { + public XSubCategoryTitleResourceProvider( + IXStringServiceProvider stringProvider + ) : base( + stringProvider: stringProvider, + item: XCategoryServiceConstants.SubCategoryTitleResourceIdentifier + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Attributes\RequireXPoweredAttribute.cs + +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using xCommons.Constants; +using xCommons.Extensions; + +namespace xCommons.Attributes { + public partial class RequireXPoweredAttribute : AuthorizeAttribute, IAuthorizationFilter { + + private readonly bool ignoreAnonymous; + + public RequireXPoweredAttribute (bool IgnoreAnonymous = true) { + ignoreAnonymous = IgnoreAnonymous; + } + + public void OnAuthorization (AuthorizationFilterContext context) { + // + var config = context.GetXAppConfiguration (); + + // + var isExistsXPowered = context.HttpContext.Request.Headers + .Any (h => h.Key + .ToNormalString () == XAuthorization.XPoweredBy.ToNormalString () && + h.Value == config.XPoweredValue); + + // + var isAnonymous = context.IsAnonymousAllowed (); + var handler = ignoreAnonymous ? isAnonymous ? false : true : true; + + // + if (handler && + !isExistsXPowered) { + context.Result = new UnauthorizedResult (); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Authorization\RequiredRolesHandler.cs + +using System.Linq; +using System.Threading.Tasks; +using IdentityModel; +using Microsoft.AspNetCore.Authorization; + +namespace xCommons.Authorization { + public partial class RequiredRolesHandler : AuthorizationHandler { + protected override Task HandleRequirementAsync ( + AuthorizationHandlerContext context, + RequiredRolesRequirement requirement + ) { + // + var conatinsRole = context.User.HasClaim (c => c.Type == JwtClaimTypes.Role); + if (!conatinsRole) { + return Task.CompletedTask; + } + + // + var roleClaim = context.User.Claims.FirstOrDefault (c => c.Type == JwtClaimTypes.Role); + if (roleClaim == null || + !requirement.Roles.Contains (roleClaim.Value) + ) { + return Task.CompletedTask; + } + + // + context.Succeed (requirement); + return Task.CompletedTask; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Authorization\RequiredRolesRequirement.cs + +using Microsoft.AspNetCore.Authorization; + +namespace xCommons.Authorization { + public partial class RequiredRolesRequirement : IAuthorizationRequirement { + public string[] Roles { get; } + public RequiredRolesRequirement (params string[] Roles) { + this.Roles = Roles; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Configurations\XAppConfiguration.cs + +using System.Collections.Generic; + +namespace xCommons.Configurations { + /// + /// represent an xBackend Application + /// + public partial class XAppConfiguration { + public string Name { get; set; } + public string Version { get; set; } + public string XPoweredValue { get; set; } + public IEnumerable AllowedOrigins { get; set; } + public string WelcomeMessage { get; set; } + public string DefaultLanguage { get; set; } + public XMessageResourceTitles MessageResourceTitles { get; set; } + } + + public partial class XMessageResourceTitles { + public string Terms { get; set; } + public string Invite { get; set; } + public string RegistrationApprove { get; set; } + public string Registered { get; set; } + public string VerificationCode { get; set; } + public string ChangePassword { get; set; } + public string PasswordChanged { get; set; } + public string NewDeviceLoggedIn { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Configurations\XSwaggerConfiguration.cs + +using System.Collections.Generic; + +namespace xCommons.Configurations { + public partial class XSwaggerConfiguration { + public string Title { get; set; } = ""; + public string Version { get; set; } = "V1.0"; + public string Description { get; set; } = ""; + public string TermsOfServiceUrl { get; set; } = ""; + + public XSwaggerContact Contact { get; set; } = null; + public XSwaggerLicence License { get; set; } = null; + public Dictionary DefaultValues {get; set;} = null; + } + + public partial class XSwaggerContact { + public string Name { get; set; } + public string Email { get; set; } + public string Url { get; set; } + } + + public partial class XSwaggerLicence { + public string Name { get; set; } + public string Url { get; set; } + } + + public partial class XSwaggerDefaultValues + { + public Dictionary Values { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Constants\CommonConstants.cs + +namespace xCommons.Constants { + public partial class CommonConstants { + public static char DEFAULT_LIST_SEPERATOR = ','; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Constants\ConfigurationNodeNames.cs + +namespace xCommons.Constants { + public partial struct ConfigurationNodeNames { + public const string CERTIFICATES_NODE = "Certificates"; + public const string SWAGGER_NODE = "SwaggerConfiguration"; + public const string MESSAGE_RESOURCE_TITLES_NODE = "MessageResourceTitles"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Constants\XAuthentication.cs + +namespace xCommons.Constants { + public partial struct XAuthentication { + public const string JWT = "jwt"; + public const string BEARER = "Bearer"; + public const string TOKEN = "token"; + public const string INTROSPECTION = "introspection"; + public const string IDENTITY_SERVER_LOCAL_API = "IdentityServerAccessToken"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Constants\XAuthorization.cs + +namespace xCommons.Constants { + public partial struct XAuthorization { + public const string TokenIdentifier = "Bearer "; + public const string Header = "Authorization"; + public const string AccessToken = "access_token"; + public const string RefreshToken = "refresh_token"; + public const string ExpiresAt = "expires_at"; + public const string AccessTokenExpired = "AccessToken-Expired"; + public const string RevisionChecksum = "revision_checksum"; + public const string XPoweredBy = "X-PoweredBy"; + public const int ThresholdBeforeTokenExpiration = 600; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Constants\XCustomClaims.cs + +namespace xCommons.Constants { + public partial struct XCustomClaims { + public const string IsEnabled = "is_enabled"; + public const string IsBanned = "is_banned"; + public const string Issuer = "iss"; + public const string ExpiredOn = "exp"; + public const string Audience = "aud"; + public const string ClientId = "client_id"; + public const string AuthenticatedOn = "auth_time"; + public const string UserId = "sub"; + public const string UserName = "unique_name"; + public const string FirstName = "given_name"; + public const string LastName = "family_name"; + public const string Gender = "gender"; + public const string Picture = "picture"; + public const string BrithDate = "birthdate"; + public const string Bio ="bio"; + public const string CoverImage = "cover_image"; + public const string OpenToSearch = "open_to_search"; + public const string Email = "email"; + public const string EmailVerified = "email_verified"; + public const string PhoneNumber = "phone_number"; + public const string PhoneNumberVerified = "phone_number_verified"; + public const string Role = "role"; + public const string Scope = "scope"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Constants\XDeviceType.cs + +namespace xCommons.Constants { + /// + /// a collection of available Client Devices + /// + public enum XDeviceType { + Unknown, + Mobile, + Tablet, + Desktop + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Constants\XFileType.cs + +namespace xCommons.Constants { + public enum XFileType { + Image, + ProfileImasge, + CoverImage, + Audio, + Video, + Document + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Constants\XPolicy.cs + +namespace xCommons.Constants { + public partial struct XPolicy { + public const string AllowedOrigins = "AllowedOrigins"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Controllers\XBaseApiController.cs + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Attributes; +using xCommons.Configurations; +using xCommons.Providers; + +namespace xCommons.Controllers { + /// + /// Base Api Controller + /// + [ApiController] + [RequireXPowered(true)] + [Route("api/[controller]")] + [Produces("application/json")] + public abstract partial class XBaseApiController : XBaseController + { + protected XBaseApiController( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Controllers\XBaseApiV1Controller.cs + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Attributes; +using xCommons.Configurations; +using xCommons.Providers; + +namespace xCommons.Controllers { + /// + /// Base Api V1 Controller + /// + [ApiController] + [ApiVersion("1.0")] + [RequireXPowered(true)] + [Route("api/v{version:apiVersion}/[controller]")] + public abstract partial class XBaseApiV1Controller : XBaseApiController + { + protected XBaseApiV1Controller( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Controllers\XBaseController.cs + +using System; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Attributes; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; + +namespace xCommons.Controllers { + /// + /// Base Controller + /// + [Route ("[controller]")] + [Produces ("application/json")] + public abstract partial class XBaseController : ControllerBase { + public ILogger Logger { get; } + public XAppConfiguration AppConfiguration { get; } + public XValidationProvider ValidationProvider { get; } + + protected XBaseController ( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) { + Logger = logger; + AppConfiguration = appConfiguration; + ValidationProvider = validationProvider; + } + + /// + /// Check the Controller Up and Running + /// + /// string message + [HttpGet] + [Route ("Test/Hi")] + [AllowAnonymous] + public virtual IActionResult Hi () { + // + var controllerName = GetControllerName (); + var message = $"{controllerName} Controller is Up and running ..."; + + // + return Ok (message); + } + + /// + /// Check Controller ByPass XPowered Filter + /// + /// string message + [HttpGet] + [AllowAnonymous] + [RequireXPowered (false)] + [Route ("Test/PassRequireXPowered")] + public virtual IActionResult PassRequireXPowered () { + // + var controllerName = GetControllerName (); + var message = $"{controllerName} Controller bypass RequiredXPowered and Up and running ..."; + + // + return Ok (message); + } + + // + #region Non Actions ... + /// + /// Convert an Exception to Propper Error Result + /// + /// + /// + [NonAction] + public ActionResult GetExceptionActionResult (Exception ex) { + // + var exception = GetExceptionResult (ex); + var error = ex.Message.ToXError (); + + // + Logger.LogError ($"exception: {exception}, error: {error}"); + + // + try { + var xError = exception.Message.ToXError (); + var xException = (XException) xError.Id; + + // + switch (xException) { + // + case XException.NotFound: + return NotFound (error); + + // + case XException.NotAuthorized: + return Unauthorized (); + } + } catch { } + + // + return BadRequest (error); + } + + /// + /// Return Structured Exception + /// + /// + /// + [NonAction] + public Exception GetExceptionResult (Exception ex) { + // + // Log Exception Message ... + Logger.LogError (ex.Message); + + // + // if Happens Exception is Know Error + // catch it and return it in BadRequest Container ... + var isEr = ex.Message.IsXError (); + + // + if (isEr) { + return ex; + } + + // + // if catched Exception Contains Unknown Error + // return an Unknown Error ... + return XException.Unknown.ToException (); + } + + /// + /// Retrieve ControllerName + /// + /// + [NonAction] + public string GetControllerName () { + return this.ControllerContext.RouteData.Values["controller"].ToString (); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\DI\XDIHelperExtension.cs + +using Microsoft.Extensions.DependencyInjection; +using xCommons.Providers; + +public static partial class XDIHelperExtension { + /// + /// Register XCommons Module Services + /// + /// + public static void AddXCommons (this IServiceCollection services) { + services.AddSingleton (); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\CertificateExtensions.cs + +using Microsoft.Extensions.Configuration; +using xCommons.Constants; +using xCommons.Models; + +namespace xCommons.Extensions { + public static partial class CertificateExtensions { + /// + /// Retrieve Specific Certificate From app Settings + /// + /// + /// + /// + public static XCertificate GetCertificate (this IConfiguration configuration, string name) { + // + var certificatesSection = configuration.GetSection (ConfigurationNodeNames.CERTIFICATES_NODE); + var certificateSection = certificatesSection.GetSection (name); + var certificate = certificateSection.Get (); + + // + return certificate; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\CommonExtensions.cs + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using System.Xml; +using AutoMapper; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using xCommons.Constants; +using xCommons.Helpers; +using xExceptions.Attributes; +using xExceptions.Constants; + +namespace xCommons.Extensions +{ + public static partial class CommonExtensions + { + /// + /// Conert XMLNode to String ... + /// + /// + /// + /// + public static string ToString(this XmlNode node, int indentation = 2) + { + // + using (var sw = new StringWriter()) + { + // + using (var xw = new XmlTextWriter(sw)) + { + xw.Formatting = System.Xml.Formatting.Indented; + xw.Indentation = indentation; + node.WriteContentTo(xw); + } + + // + return sw.ToString(); + } + } + + /// + /// Converts a Json String to Dictionary of String Mappings + /// + /// Json String + /// + public static Dictionary JSONToDictionary(this string source) + { + // + try + { + return JsonConvert.DeserializeObject>(source); + } + catch + { + return null; + } + } + + /// + /// Convert properties of specific Class instance to Dictionary ... + /// + /// class instance + /// + public static Dictionary ToDictionary(this T source) where T : class + { + // + var result = new Dictionary(); + + // + if (source.IsNull()) + { + return result; + } + + // + PropertyInfo[] propertyInfos = source.GetType().GetProperties(); + if (!propertyInfos.HasChild()) + { + return result; + } + + // + foreach (PropertyInfo propertyInfo in propertyInfos) + { + result.Add( + propertyInfo.Name, + propertyInfo + .GetValue(source, null) + .ToString() + ); + } + + // + return result; + } + + /// + /// run specific task and retrieve result ... + /// + /// + public static void RunTask(this Task source) + { + source. + GetAwaiter() + .GetResult(); + } + + /// + /// run specific task and retrieve result ... + /// + /// + public static T RunTask(this Task source) + { + return source. + GetAwaiter() + .GetResult(); + } + + /// + /// Convert XML String to Json Object ... + /// + /// + /// + public static string ToJSON(this string source) + { + // + var result = string.Empty; + try + { + // + result = XMLToJSONHelper.ToJSON(source); + + // + // Handle Removing Additional Values ... + result = result.Replace("s:", "").Replace("a:", ""); + } + catch { } + + // + return result; + } + + /// + /// Convert object to Json ... + /// + /// + /// + public static string ToJSON(this T value) + where T : class + { + // + if (value.IsNull()) + { + return string.Empty; + } + + // + var setting = new JsonSerializerSettings + { + Formatting = Newtonsoft.Json.Formatting.Indented, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + PreserveReferencesHandling = PreserveReferencesHandling.None + }; + + // + return JsonConvert.SerializeObject(value, Newtonsoft.Json.Formatting.None, setting); + } + + /// + /// Convert to Json With Support of Camel Casing + /// + /// + /// + /// + public static string ToJSON(this T value, bool camelCase = false) + { + // + if (value.IsNull()) + { + return string.Empty; + } + + // + var setting = new JsonSerializerSettings + { + Formatting = Newtonsoft.Json.Formatting.Indented, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + PreserveReferencesHandling = PreserveReferencesHandling.None, + }; + + // + if (camelCase) + { + setting.ContractResolver = new CamelCasePropertyNamesContractResolver(); + } + + // + return JsonConvert.SerializeObject(value, Newtonsoft.Json.Formatting.None, setting); + } + + /// + /// Map to Dynamic Object ... + /// + /// + /// + public static dynamic ToDynamicObject(this string source) + { + return JsonConvert.DeserializeObject(source); + } + + /// + /// Map to Dynamic Object ... + /// + /// + /// + public static dynamic ToDynamicObject(this object source) + { + // + var dMapConfig = new MapperConfiguration(b => { }); + var dMapper = dMapConfig.CreateMapper(); + + // + return dMapper.Map(source); + } + + /// + /// Parse From Dynamic Object ... + /// + /// + /// + /// + public static T FromDynamicObject(this object source) + where T : class + { + // + var dMapConfig = new MapperConfiguration(b => { }); + var dMapper = dMapConfig.CreateMapper(); + dynamic dSource = source; + + // + return dMapper.Map(dSource); + } + + /// + /// Convert Type of Object to Safe Type + /// + /// + /// + /// + public static T ConvertTo(this object source) + { + // + T result = default(T); + + // + try + { + // + T item = (T)Convert.ChangeType(source, typeof(T)); + + // + result = item; + } + catch { } + + // + return result; + } + + /// + /// Convert an Instance to another Instance + /// using AutoMapper dynamic Convertion + /// + /// + /// + /// + public static T MapConvert(this T source) + where T : class + { + // + if (source.IsNull()) + { + return null; + } + + // + var dSource = (object)source.ToDynamicObject(); + return dSource.FromDynamicObject(); + } + + /// + /// Convert Using Auto Mapper ... + /// + /// + /// + /// + /// + public static T MapConvert(this E source) + where T : class + where E : class + { + // + if (source.IsNull()) + { + return null; + } + + // + var dMapConfig = new MapperConfiguration(b => + { + b.CreateMap() + .ReverseMap(); + }); + var dMapper = dMapConfig.CreateMapper(); + + // + var dSource = (object)dMapper.Map(source); + var result = dMapper.Map(dSource); + + // + return result; + } + + /// + /// Convert Using Auto Mapper ... + /// + /// + /// + /// + /// + public static IEnumerable MapConvert(this IEnumerable source) + { + // + if (source.IsNull()) + { + return null; + } + + // + var dMapConfig = new MapperConfiguration(b => + { + b.CreateMap() + .ReverseMap(); + }); + var dMapper = dMapConfig.CreateMapper(); + + // + var dSource = (object)dMapper.Map(source); + var result = dMapper.Map>(dSource); + + // + return result; + } + + /// + /// Convert Using Auto Mapper ... + /// + /// + /// + /// + /// + public static ICollection MapConvert(this ICollection source) + { + // + if (source.IsNull()) + { + return null; + } + + // + var dMapConfig = new MapperConfiguration(b => + { + b.CreateMap() + .ReverseMap(); + }); + var dMapper = dMapConfig.CreateMapper(); + + // + var dSource = (object)dMapper.Map(source); + var result = dMapper.Map>(dSource); + + // + return result; + } + + /// + /// Convert Using Auto Mapper ... + /// + /// + /// + /// + /// + public static IList MapConvert(this IList source) + { + // + if (source.IsNull()) + { + return null; + } + + // + var dMapConfig = new MapperConfiguration(b => + { + b.CreateMap() + .ReverseMap(); + }); + var dMapper = dMapConfig.CreateMapper(); + + // + var dSource = (object)dMapper.Map(source); + var result = dMapper.Map>(dSource); + + // + return result; + } + + /// + /// Convert Using Auto Mapper ... + /// + /// + /// + /// + /// + /// + public static IDictionary MapConvert(this IDictionary source) + { + // + if (source.IsNull()) + { + return null; + } + + // + var dMapConfig = new MapperConfiguration(b => + { + b.CreateMap() + .ReverseMap(); + }); + var dMapper = dMapConfig.CreateMapper(); + + // + var dSource = (object)dMapper.Map(source); + var result = dMapper.Map>(dSource); + + // + return result; + } + + /// + /// Convert a Type to Static Type + /// + /// + /// + /// + public static T ToStatic(this object source) + { + // + var entity = Activator.CreateInstance(); + + // + var properties = source as IDictionary; + + // + if (properties == null) + { + return entity; + } + + // + foreach (var entry in properties) + { + // + var propertyInfo = entity.GetType().GetProperty(entry.Key); + if (propertyInfo != null) + { + propertyInfo.SetValue(entity, entry.Value, null); + } + } + + // + return entity; + } + + /// + /// Convert From Json String ... + /// + /// + /// + /// + public static T FromJSON(this string value) + { + return JsonConvert.DeserializeObject(value); + } + + /// + /// Convert From Json String ... + /// + /// + /// + /// + public static object FromJSON( + this string value, + Type type + ) + { + return JsonConvert + .DeserializeObject(value, type); + } + + /// + /// Search a Json String for Specific Node ... + /// + /// + /// + /// + public static List FindTokens(this JToken containerToken, string name) + { + List matches = new List(); + FindTokens(containerToken, name, matches); + return matches; + } + + /// + /// Convert to MD5 Bytes ... + /// + /// + /// + public static byte[] ToMd5Bytes(this string source) + { + return new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(source)); + } + + /// + /// Convert to MD5 String ... + /// + /// + /// + public static string ToMd5String(this string source) + { + // + // string representation (similar to UNIX format) + return BitConverter.ToString(source.ToMd5Bytes()) + // without dashes + .Replace("-", string.Empty) + // make lowercase + .ToLower(); + } + + /// + /// Check a String is MD5 String or not ... + /// + /// + /// + public static bool IsMD5String(this string source) + { + // + if (String.IsNullOrEmpty(source)) + { + return false; + } + + // + return Regex.IsMatch(source, "^[0-9a-fA-F]{32}$", RegexOptions.Compiled); + } + + /// + /// Convert an string to Byte array Representation ... + /// + /// + /// + public static byte[] ToBytes(this string source) + { + return Encoding.UTF8.GetBytes(source); + } + + /// + /// Converts Byte Array to String Value ... + /// + /// + /// + public static string FromBytes(this byte[] source) + { + // + var result = Encoding.UTF8.GetString(source, 0, source.Length); + return result; + } + + /// + /// Check object is Null ... + /// + /// + /// + public static bool IsNull(this object source) + { + return source == null; + } + + /// + /// Check collection Has Childs ... + /// + /// + /// + /// + public static bool HasChild(this IList source) + { + return !source.IsNull() && source.Count() > 0; + } + + /// + /// Check collection Has Childs ... + /// + /// + /// + /// + public static bool HasChild(this ICollection source) + { + return !source.IsNull() && source.Count() > 0; + } + + /// + /// Check collection Has Childs ... + /// + /// + /// + /// + public static bool HasChild(this IEnumerable source) + { + return !source.IsNull() && source.Count() > 0; + } + + /// + /// Select Async ... + /// + /// + /// + /// + /// + /// + public static async Task> SelectAsync( + this IEnumerable source, Func> method + ) + { + return await Task.WhenAll(source.Select(async s => await method(s))); + } + + /// + /// Converts an Enumerable to Async Enumerable ... + /// + /// + /// + /// + /// + public static async IAsyncEnumerable ToAsyncEnumerable( + this IEnumerable source, + [System.Runtime.CompilerServices.EnumeratorCancellation] + CancellationToken cancellationToken = default + ) + { + // + foreach (var item in source) + { + // + if (cancellationToken.IsCancellationRequested) + { + yield break; + } + + // + yield return item; + } + } + + /// + /// Converts an Stream to Model AsyncEnumerator ... + /// + /// + /// + /// + /// + public static async IAsyncEnumerable ToModelAsyncEnumerable( + this Stream source, + [System.Runtime.CompilerServices.EnumeratorCancellation] + CancellationToken cancellationToken = default + ) + { + // + // Validate Stream ... + if (source.IsNull()) + { + yield break; + } + + // + while (source.CanRead) + { + // + var model = await source + .ReadModelFromStream(cancellationToken); + + // + yield return model; + } + } + + /// + /// Converts an Stream to String AsyncEnumerator ... + /// using Read Line of Stream Reader ...S + /// + /// + /// + /// + public static async IAsyncEnumerable ToStringAsyncEnumerable( + this Stream source, + [System.Runtime.CompilerServices.EnumeratorCancellation] + CancellationToken cancellationToken = default + ) + { + // + // Validate Stream ... + if (source.IsNull()) + { + yield break; + } + + // + while (source.CanRead) + { + // + var json = await source + .ReadJsonFromStream(cancellationToken); + + // + yield return json; + } + } + + /// + /// Write a Model to Stream ... + /// + /// an Stream to Write ... + /// a Model instance to Write ... + /// + /// + /// + public static async Task WriteModelToStream( + this Stream source, + T model, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + if (source.IsNull() || model.IsNull()) + { + return; + } + + // + // Extract Json of Model ... + var json = model.ToJSON(true); + + // + await source.WriteJsonToStream( + json: json, + cancellationToken: cancellationToken + ); + } + + /// + /// Write a Json String to Stream ... + /// + /// an Stream to Write ... + /// json string to write ... + /// + /// + public static async Task WriteJsonToStream( + this Stream source, + string json, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + if (source.IsNull() || json.IsNullOrEmpty()) + { + return; + } + + // + using (var streamWriter = new StreamWriter(stream: source)) + { + // + using (var jsonWriter = new JsonTextWriter(streamWriter)) + { + // + await jsonWriter.WriteRawAsync(json, cancellationToken); + await jsonWriter.FlushAsync(cancellationToken); + } + } + } + + /// + /// Read a Json Model From Stream ... + /// + /// + /// + /// + /// + public static async Task ReadModelFromStream( + this Stream source, + CancellationToken cancellationToken = default + ) + { + // + T result = default(T); + + // + var json = await source.ReadJsonFromStream(cancellationToken); + if (!json.IsNullOrEmpty()) + { + // + try + { + result = json.FromJSON(); + } + catch + { } + } + + // + return result; + } + + /// + /// Reading Json String From Stream ... + /// + /// an Stream to Write ... + /// + /// + public static async Task ReadJsonFromStream( + this Stream source, + CancellationToken cancellationToken = default + ) + { + // + var result = string.Empty; + + // + // Validate Args ... + if (source.IsNull()) + { + return result; + } + + // + using (var streamReader = new StreamReader(source)) + { + // + result = await Task.Run(() => + { + return streamReader.ReadLine(); + }, + cancellationToken); + } + + // + return result; + } + + /// + /// Update a Collection item ... + /// + /// + /// + /// + /// + /// + public static IList Update( + this IList source, + T sourceItem, + T updateWith + ) + { + // + var result = new List(); + + // + // Validate Args ... + if (!source.HasChild() || + updateWith.IsNull() || + sourceItem.IsNull() + ) + { + return result; + } + + // + // Extract Item from source ... + result = source + .Except(new List() { sourceItem }) + .ToList(); + result.Add(updateWith); + + // + return source; + } + + /// + /// Update a Collection item ... + /// + /// + /// + /// + /// + /// + public static ICollection Update( + this ICollection source, + T sourceItem, + T updateWith + ) + { + // + var result = new List(); + + // + // Validate Args ... + if (!source.HasChild() || + updateWith.IsNull() || + sourceItem.IsNull() + ) + { + return result; + } + + // + // Extract Item from source ... + result = source + .Except(new List() { sourceItem }) + .ToList(); + result.Add(updateWith); + + // + return source; + } + + /// + /// Update a Collection item ... + /// + /// + /// + /// + /// + /// + public static IEnumerable Update( + this IEnumerable source, + T sourceItem, + T updateWith + ) + { + // + var result = new List(); + + // + // Validate Args ... + if (!source.HasChild() || + updateWith.IsNull() || + sourceItem.IsNull() + ) + { + return result; + } + + // + // Extract Item from source ... + result = source + .Except(new List() { sourceItem }) + .ToList(); + result.Add(updateWith); + + // + return source; + } + + /// + /// Select and Retrieve Nth Childs of given List ... + /// + /// + /// + /// + /// + /// + public static IEnumerable GetNthItems(this IList source, int n, bool throwException = false) + { + // + var result = new List(); + if (n < 1 || n > source.Count || source.IsNull() || !source.HasChild()) + { + if (throwException) + { + throw XException.InvalidArgs.ToException(); + } + else + { + return result; + } + } + + // + var index = 0; + foreach (var item in source) + { + // + result.Add(item); + index++; + + // + if (index >= n) + { + break; + } + } + + // + return result; + } + + /// + /// Select and Retrieve Nth Childs of given List ... + /// + /// + /// + /// + /// + /// + public static IEnumerable GetNthItems(this ICollection source, int n, bool throwException = false) + { + return source.ToList().GetNthItems( + n: n, + throwException: throwException + ); + } + + /// + /// Select and Retrieve Nth Childs of given List ... + /// + /// + /// + /// + /// + /// + public static IEnumerable GetNthItems(this IEnumerable source, int n, bool throwException = false) + { + return source.ToList().GetNthItems( + n: n, + throwException: throwException + ); + } + + /// + /// Check an string is Null or Empty ... + /// + /// + /// + public static bool IsNullOrEmpty(this string sourse) + { + return string.IsNullOrEmpty(sourse); + } + + /// + /// Get string Value Attribute of an Enum ... + /// + /// + /// + public static string GetStringValue(this Enum source) + { + var type = source.GetType(); + var fieldInfo = type.GetField(source.ToString()); + var attributes = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[]; + + return attributes != null && attributes.Length > 0 ? + attributes[0].StringValue : null; + } + + /// + /// Extracts digit's of an string ... + /// + /// + /// + public static string GetDigits(this string source) + { + try + { + // + Regex regexObj = new Regex(@"[^\d]"); + return regexObj.Replace(source, ""); + } + catch + { + return null; + } + } + + /// + /// Check an string is contains only digits ... + /// + /// + /// + public static bool IsDigits(this string source) + { + return (source.GetDigits() != null && + source.GetDigits().ToString() == source); + } + + /// + /// Convert to normal string, Trim and ToLower ... + /// + /// + /// + public static string ToNormalString(this string source) + { + // + if (source.IsNullOrEmpty()) + { + return string.Empty; + } + + // + return source.Trim().ToLowerInvariant(); + } + + /// + /// Capitalize an string ... + /// + /// + /// + public static string Capitalize(this string source) + { + // + if (source.IsNullOrEmpty()) + { + return string.Empty; + } + + // + var result = ""; + if (source.Length > 1) + { + // + var firstCharr = source.Substring(0, 1); + var otherParts = source.Substring(1, source.Length - 1); + + // + result = firstCharr.ToUpperInvariant() + otherParts; + } + else + { + result = source.ToUpperInvariant(); + } + + // + return result; + } + + /// + /// Summarize a Text ... + /// + /// + /// + /// + /// + public static string SummarizeContent( + this string source, + int maxAllowedLength = 0, + string endsWith = "" + ) + { + // + var result = string.Empty; + if (source.IsNullOrEmpty() || maxAllowedLength <= 0) + { + // + result = source; + return result; + } + + // + // Pattern of Links ... + var pattern = @"(?:[!]\[(.*?)\]\(.*?\))"; + + // + // Apply Summarization ... + + // + // Replace Markdown Links using RegExp ... + result = Regex.Replace(source, pattern, ""); + + // + // Do Summarization if Length bigger than MaxLength ... + if (result.Length > maxAllowedLength) + { + result = result.Substring(0, maxAllowedLength); + } + + // + // Apply EndsWith if Provided ... + if (!endsWith.IsNullOrEmpty() && + !result.EndsWith(endsWith)) + { + result += endsWith; + } + + // + return result; + } + + /// + /// Check an string is a Valid Representation of PhoneNumber ... + /// + /// + /// + public static bool IsValidMobileNumber(this string source) + { + // + // Validating Mobile Numbers ... + var phoneNumberUtil = PhoneNumbers.PhoneNumberUtil.GetInstance(); + try + { + // + var mPhoneNumber = phoneNumberUtil.Parse(source, null); + if (phoneNumberUtil.IsValidNumber(mPhoneNumber)) + { + // + return true; + } + + // + return false; + } + catch + { + + // + return false; + } + } + + /// + /// Check an string is Valid Representation of Email Address ... + /// + /// + /// + public static bool IsValidEmail(this string source) + { + try + { + var addr = new System.Net.Mail.MailAddress(source); + return addr.Address == source; + } + catch + { + return false; + } + } + + /// + /// Check an string is Valid Representation of URL Address ... + /// + /// + /// + public static bool IsValidUrl(this string url) + { + return Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute); + } + + /// + /// check a URL has / at the end, if not add one ... + /// + /// + /// + public static string NormalizeUrl(this string url) + { + return url.EndsWith("/") ? url : url + "/"; + } + + /// + /// Check an string is GUID or not ... + /// + /// + /// + public static bool IsGuid(this string source) + { + // + // Validate Args ... + if (source.IsNullOrEmpty()) + { + return false; + } + + // + Guid guid; + var result = Guid.TryParse(source, out guid); + + // + return result; + } + + /// + /// Check Specific Guid string is Default Guid or not ... + /// + /// + /// + public static bool IsDefaultGuid(this string guid) + { + // + if (!guid.IsGuid()) + { + return false; + } + + // + var empty = Guid.Empty.ToString(); + return guid == empty; + } + + /// + /// Check Specific Guid is Default Guid or not ... + /// + /// + /// + public static bool IsDefaultGuid(this Guid guid) + { + // + var empty = Guid.Empty; + return guid == empty; + } + + /// + /// Parse an String to related Enumerable Items ... + /// + /// + /// + /// + /// + public static IEnumerable ParseListString( + this string source, + char? separator = null + ) + { + // + if (!separator.HasValue) + { + separator = CommonConstants.DEFAULT_LIST_SEPERATOR; + } + + // + var list = source.Split(separator.Value); + var result = new Collection(); + + // + foreach (var s in list) + { + // + try + { + // + T item = (T)Convert.ChangeType(s, typeof(T)); + + // + result.Add(item); + } + catch { } + } + + // + return result; + } + + /// + /// Converts a List String Representation to GUid List ... + /// + /// + /// + /// + public static IEnumerable ParseListGuid( + this string list, + char? separator = null + ) + { + // + var result = new List(); + + // + if (!separator.HasValue) + { + separator = CommonConstants.DEFAULT_LIST_SEPERATOR; + } + + // + if (list.IsNullOrEmpty()) + { + return result; + } + + // + var items = list.Split(separator.Value); + items.ToList().ForEach(i => + { + // + if (!i.IsNullOrEmpty() && i.IsGuid() && !i.IsDefaultGuid()) + { + // + var gID = new Guid(i); + result.Add(gID); + } + }); + + // + return result; + } + + /// + /// Convert a List to List String ... + /// + /// + /// + /// + /// + public static string ToListString( + this IEnumerable source, + char? separator = null + ) + { + // + if (!separator.HasValue) + { + separator = CommonConstants.DEFAULT_LIST_SEPERATOR; + } + + // + if (!source.HasChild()) + { + return string.Empty; + } + + // + string result = String.Join( + separator + .ToString(), + source + ); + + // + return result; + } + + /// + /// get default value of a Type + /// + /// + /// + /// + public static T Default(this T source) + where T : class + { + return default(T); + } + + /// + /// Check if an object is Null or Default Value + /// + /// + /// + /// + public static bool IsNullOrDefault(this T source) + where T : class + { + return Equals(source, default(T)); + } + + /// + /// convert a timestamp to ISO String representation ... + /// + /// + /// + public static string ISODateString(this long timestamp) + { + // + var time = timestamp.FromTimestamp(); + var result = time.ToString("o"); + + // + return result; + } + + /// + /// Converts a DateTime to Unix Like Long ... + /// + /// + /// + public static long ToTimestamp( + this DateTime source + ) + { + return new DateTimeOffset(source) + .ToUnixTimeMilliseconds(); + } + + /// + /// convert timestamp to DateTime ... + /// + /// + /// + public static DateTime FromTimestamp(this long timestamp) + { + // + var result = DateTimeOffset.FromUnixTimeMilliseconds(timestamp); + return result.DateTime; + } + public static DateTime FromTimestamp(this string source) + { + // + var timestamp = Int64.Parse(source); + var time = timestamp.FromTimestamp(); + return time; + } + + /// + /// Check a Date Time is Expired or not + /// + /// + /// + public static bool IsExPired(this DateTime source) + { + // + if (source.IsNull()) + { + return true; + } + + // + var currentDate = DateTime.UtcNow; + var isExpired = currentDate >= source; + + // + return isExpired; + } + public static bool IsExpired(this long source) + { + var time = source.FromTimestamp(); + return time.IsExPired(); + } + public static bool IsExpired(this string source) + { + // + var time = DateTime.Parse(source); + return time.IsExPired(); + } + + /// + /// Determines a File Type is Image Kind or not + /// + /// + /// + public static bool IsImageKind(this XFileType source) + { + // + var imageKind = new Collection() { + XFileType.Image, + XFileType.CoverImage, + XFileType.ProfileImasge + }; + + // + return imageKind.Contains(source); + } + + /// + /// Encode String as Url + /// + /// + /// + public static string ToUrlEncoded(this string source) + { + return HttpUtility.UrlEncode(source); + } + + /// + /// Decode Url String + /// + /// + /// + public static string ToUrlDecoded(this string source) + { + return HttpUtility.UrlDecode(source); + } + + /// + /// Convert an Object to string for use in HttpParams + /// + /// + /// + /// + public static string ToHttpParamString(this object value, CultureInfo cultureInfo) + { + // + if (value is Enum) + { + // + string name = Enum.GetName(value.GetType(), value); + if (name != null) + { + // + var field = IntrospectionExtensions + .GetTypeInfo(value.GetType()) + .GetDeclaredField(name); + + // + if (field != null) + { + // + var attribute = CustomAttributeExtensions + .GetCustomAttribute(field, typeof(EnumMemberAttribute)) + as EnumMemberAttribute; + + // + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + } + } + else if (value is bool) + { + return System.Convert.ToString(value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[])value); + } + else if (value != null && value.GetType().IsArray) + { + // + var array = Enumerable.OfType((Array)value); + return string.Join(",", Enumerable + .Select( + array, + o => o.ToHttpParamString(cultureInfo))); + } + + // + return System.Convert.ToString(value, cultureInfo); + } + + /// + /// ReShape an Object as HttpParam String + /// + /// + /// + public static string AsHttpParamString(this object value) + { + return System.Uri + .EscapeDataString(value + .ToHttpParamString(CultureInfo.InvariantCulture)); + } + + /// + /// Calculating Date Difference Provider String + /// and Generate Date based on UTC Time in Milliseconds like unix Date ... + /// + /// + /// + public static long GenerateDateTimeOffset(this string offsetProvider) + { + // + if (offsetProvider.IsNullOrEmpty()) + { + return 0; + } + + // + var mExpireValue = 0; + var mExpireValueStr = offsetProvider.GetDigits(); + var mExpireUnit = offsetProvider.Replace(mExpireValueStr, ""); + if (mExpireValueStr.IsNullOrEmpty() || mExpireUnit.IsNullOrEmpty()) + { + return 0; + } + + // + var isParsed = int.TryParse(mExpireValueStr, out mExpireValue); + if (!isParsed) + { + return 0; + } + + // + DateTime? date = null; + switch (mExpireUnit) + { + case "d": + date = DateTime.UtcNow.AddDays(mExpireValue); + break; + + case "h": + date = DateTime.UtcNow.AddHours(mExpireValue); + break; + + case "m": + date = DateTime.UtcNow.AddMinutes(mExpireValue); + break; + + case "s": + date = DateTime.UtcNow.AddSeconds(mExpireValue); + break; + + case "ms": + default: + date = DateTime.UtcNow.AddMilliseconds(mExpireValue); + break; + } + + // + var result = !date.HasValue ? 0 : new DateTimeOffset(date.Value) + .ToUnixTimeMilliseconds(); + + // + return result; + } + + // + #region Int Id Extensions ... + public static bool IsValidIntId(this int source) + { + return source > 0; + } + + public static void ValidateIntId(this int source) + { + // + if (!source.IsValidIntId()) + { + XException.InvalidArgs.Throw(); + } + } + #endregion + + // + #region StringExtensions for Identity Server ... + public static string EnsureTrailingSlash(this string input) + { + if (!input.EndsWith("/")) + { + return input + "/"; + } + + return input; + } + + public static bool IsMissing(this string value) + { + return string.IsNullOrWhiteSpace(value); + } + + public static bool IsPresent(this string value) + { + return !string.IsNullOrWhiteSpace(value); + } + + public static string Sha256(this string input) + { + if (input.IsMissing()) return string.Empty; + + using (var sha = SHA256.Create()) + { + var bytes = Encoding.UTF8.GetBytes(input); + var hash = sha.ComputeHash(bytes); + + return Convert.ToBase64String(hash); + } + } + #endregion + + // + #region Private ... + private static void FindTokens(JToken containerToken, string name, List matches) + { + if (containerToken.Type == JTokenType.Object) + { + foreach (JProperty child in containerToken.Children()) + { + if (child.Name == name) + { + matches.Add(child.Value); + } + FindTokens(child.Value, name, matches); + } + } + else if (containerToken.Type == JTokenType.Array) + { + foreach (JToken child in containerToken.Children()) + { + FindTokens(child, name, matches); + } + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\DIExtensions.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using Swashbuckle.AspNetCore.SwaggerUI; +using xCommons.Configurations; +using xCommons.Constants; +using xCommons.Filters; + +namespace xCommons.Extensions { + public static partial class DIExtensions { + + /// + /// Inject Specific Registered Service from IServiceCollection + /// + /// + /// + /// + public static T GetRegisteredService (this IServiceCollection source) { + // + var serviceProvider = source.BuildServiceProvider (); + return serviceProvider.GetService (); + } + + /// + /// Register App Configuration as a Service + /// + /// + /// + public static void AddXAppConfiguration (this IServiceCollection services, IConfiguration configuration) { + // + var appConfiguration = configuration.GetXAppConfiguration (); + services.AddSingleton (appConfiguration); + } + + /// + /// Add Required OperationFilters and Authorization Fields to Swagger Options + /// + /// + /// + /// + /// + /// + public static void AddXSwaggerGenOptions ( + this SwaggerGenOptions source, + string xmlFilePath = null, + bool addRequiredXPoweredFilter = true, + bool addXTokenAuthorization = true, + bool addDefaultValueFilter = true + ) { + // + if (source.IsNull ()) { + source = new SwaggerGenOptions (); + } + + // + // Add Xml File Path ... + if (!xmlFilePath.IsNullOrEmpty ()) { + source.IncludeXmlComments (xmlFilePath); + } + + // + // Handle RequireXPowered Filter ... + if (addRequiredXPoweredFilter) { + source.OperationFilter (); + } + + // + // Add Default Values Filter ... + if (addDefaultValueFilter) { + source.OperationFilter (); + } + + // + // Handle XToken Authorizations ... + if (addXTokenAuthorization) { + // + #region Bearer AccessToken ... + // + source.AddSecurityDefinition (nameof (XAuthorization.AccessToken), new OpenApiSecurityScheme { + In = ParameterLocation.Header, + Description = "Bearer token", + Type = SecuritySchemeType.ApiKey, + Name = XAuthorization.Header + }); + + // + source.AddSecurityRequirement (new OpenApiSecurityRequirement { + { + new OpenApiSecurityScheme { + Reference = new OpenApiReference { + Id = nameof (XAuthorization.AccessToken), + Type = ReferenceType.SecurityScheme + } + }, Array.Empty () + } + }); + #endregion + + // + #region RefreshToken ... + // + source.AddSecurityDefinition (nameof (XAuthorization.RefreshToken), new OpenApiSecurityScheme { + In = ParameterLocation.Header, + Description = "refresh token", + Type = SecuritySchemeType.ApiKey, + Name = nameof (XAuthorization.RefreshToken) + }); + + // + source.AddSecurityRequirement (new OpenApiSecurityRequirement { + { + new OpenApiSecurityScheme { + Reference = new OpenApiReference { + Id = nameof (XAuthorization.RefreshToken), + Type = ReferenceType.SecurityScheme + } + }, Array.Empty () + } + }); + #endregion + + // + #region ExpiresAt ... + // + source.AddSecurityDefinition (nameof (XAuthorization.ExpiresAt), new OpenApiSecurityScheme { + In = ParameterLocation.Header, + Description = "expires at", + Type = SecuritySchemeType.ApiKey, + Name = nameof (XAuthorization.ExpiresAt) + }); + + // + source.AddSecurityRequirement (new OpenApiSecurityRequirement { + { + new OpenApiSecurityScheme { + Reference = new OpenApiReference { + Id = nameof (XAuthorization.ExpiresAt), + Type = ReferenceType.SecurityScheme + } + }, Array.Empty () + } + }); + #endregion + } + } + + /// + /// Register Swagger + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXSwagger ( + this IServiceCollection source, + IConfiguration configuration, + SwaggerGenOptions settings = null, + string xmlFilePath = null, + bool addRequiredXPoweredFilter = true, + bool addXTokenAuthorization = true, + bool addDefaultValueFilter = true + ) { + // + var xSwaggerConfig = configuration.GetXSwaggerConfiguration (); + if (xSwaggerConfig.IsNull ()) { + xSwaggerConfig = new XSwaggerConfiguration (); + } + + // + source.AddSingleton(xSwaggerConfig); + + // + #region Prepare SwaggerGenOptions ... + if (settings.IsNull ()) { + settings = new SwaggerGenOptions (); + } + settings.AddXSwaggerGenOptions ( + xmlFilePath: xmlFilePath, + addDefaultValueFilter: addDefaultValueFilter, + addXTokenAuthorization: addXTokenAuthorization, + addRequiredXPoweredFilter: addRequiredXPoweredFilter + ); + #endregion + + // + #region Prepare Swagger Doc ... + // + // Api Document Section ... + var apiDoc = new OpenApiInfo { + Title = xSwaggerConfig.Title, + Version = xSwaggerConfig.Version, + Description = xSwaggerConfig.Description, + }; + + // + // Api Document TermsOfUse URL ... + if (!xSwaggerConfig.TermsOfServiceUrl.IsNullOrEmpty ()) { + apiDoc.TermsOfService = new Uri (xSwaggerConfig.TermsOfServiceUrl); + } + + // + // Contact Section ... + if (!xSwaggerConfig.Contact.IsNull ()) { + // + var apiContact = new OpenApiContact { + Name = xSwaggerConfig.Contact.Name, + Email = xSwaggerConfig.Contact.Email + }; + + // + // Contact URL ... + if (!xSwaggerConfig.Contact.Url.IsNullOrEmpty ()) { + apiContact.Url = new Uri (xSwaggerConfig.Contact.Url); + } + + // + apiDoc.Contact = apiContact; + } + + // + // License Section ... + if (!xSwaggerConfig.License.IsNull ()) { + // + var apiLicense = new OpenApiLicense { + Name = xSwaggerConfig.License.Name, + }; + + // + // License URL ... + if (!xSwaggerConfig.License.Url.IsNullOrEmpty ()) { + apiLicense.Url = new Uri (xSwaggerConfig.License.Url); + } + + // + apiDoc.License = apiLicense; + } + #endregion + + // + // Register Swagger ... + source.AddSwaggerGen ( + opt => { + // + opt.UpdateData (settings); + + // + opt.SwaggerDoc ("v1", apiDoc); + } + ); + + // + // Register Swagger Operation Filters ... + } + + /// + /// Use Sagger + /// + /// + /// + public static void UseXSwagger ( + this IApplicationBuilder source, + SwaggerUIOptions options = null + ) { + // + source.UseSwagger (); + + // + if (options.IsNull ()) { + source.UseSwaggerUI (); + } else { + source.UseSwaggerUI (options: options); + } + } + + /// + /// Add Cross Origin Resource Sharings + /// + /// + /// + public static void AddXCors ( + this IServiceCollection source, + IEnumerable allowedOrigins + ) { + // + if (!allowedOrigins.HasChild ()) { + // + Console.WriteLine ($"XCommons: there is no provided allowedOrigins, start app without any specific cors, this may be unsecure ..."); + + // + source.AddCors (options => { + options.AddPolicy (XPolicy.AllowedOrigins, + builder => { + builder + .AllowAnyOrigin () + .AllowAnyHeader () + .AllowAnyMethod () + .WithExposedHeaders ("*"); + }); + }); + + // + return; + } + + // + source.AddCors (options => { + options.AddPolicy (XPolicy.AllowedOrigins, + builder => { + builder + .WithOrigins (allowedOrigins.ToArray ()) + .AllowAnyHeader () + .AllowAnyMethod () + .AllowCredentials () + .WithExposedHeaders ("*"); + }); + }); + } + + /// + /// Use X Registered Cross Origin Resource Sharings + /// + /// + public static void UseXCors (this IApplicationBuilder sources) { + // + sources.UseCors (XPolicy.AllowedOrigins); + } + + /// + /// Register Api V1 Versioning ... + /// + /// + public static void AddXApiV1Versioning(this IServiceCollection services) + { + // + // Register Api Versioning ... + services.AddApiVersioning(opt => + { + // + // Set Default Api Version ... + opt.DefaultApiVersion = new ApiVersion(1, 0); + + // + // Set Routing to Default API Version, if Version unspecified ... + opt.AssumeDefaultVersionWhenUnspecified = true; + + // + // Report All Available Api Versions on Response ... + opt.ReportApiVersions = true; + }); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\EnumExtensions.cs + +using System; +using System.Collections.Generic; + +namespace xCommons.Extensions { + public static partial class EnumExtensions { + public static string GetKey (this Enum source) { + return Enum.GetName (source.GetType (), source); + } + + public static T GetValue (this string source) { + return (T) Enum.Parse (typeof (T), source); + } + + public static void Iterate (this IEnumerator source, Action action) { + // + while (source.MoveNext ()) { + // + var current = source.Current; + action (current); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\ExceptionExtensions.cs + +using System; +using xExceptions.Constants; +using xExceptions.Models; + +namespace xCommons.Extensions { + /// + /// this is an extension pack for Exceptions + /// + public static partial class ExceptionExtensions { + + /// + /// determines an string contains an XError content or not + /// + /// + /// + public static bool IsXError (this string source) { + // + if (source.IsNullOrEmpty ()) { + return false; + } + + // + var xError = source.ToXError (); + if (xError != null) { + return true; + } + + // + // If Convert Proccess fails ... + var normalString = source.ToNormalString (); + + // + // Return result based on string values ... + return normalString.Contains ("id") && normalString.Contains ("message"); + } + + /// + /// deserialize an string to to XError class instance + /// + /// + /// + public static XError ToXError (this string source) { + // + if (source.IsNullOrEmpty ()) { + return null; + } + + // + try { + // + var err = source.FromJSON (); + + // + // if err is null we have to manually get value + if (err == null) { + // + var normalString = source.ToNormalString (); + var parts = normalString.Split (','); + if (parts.Length > 2) { + return null; + } + + // + var idContainer = parts[0]; + var messageContainer = parts[1]; + } + + // + return err; + } catch { + return null; + } + } + + public static XError ToXError (this XException source) { + // + var errorId = (int) source; + var errorMessage = source.GetStringValue (); + + // + return new XError { + Id = errorId, + Message = errorMessage + }; + } + + /// + /// Convert an XError instance to an Exception + /// + /// + /// + public static Exception ToException (this XError source) { + // + var jsonStr = source.ToJSON (); + return new Exception (jsonStr); + } + + public static Exception ToException (this XException source) { + return source + .ToXError () + .ToException (); + } + + /// + /// Convert an Exception to corresponding XError Object + /// + /// + /// + public static XError FromException (this Exception source) { + // + if (source == null || source.Message.IsNullOrEmpty ()) { + return null; + } + + // + return source.Message.ToXError (); + } + + /// + /// Add Content to Contentable XException member and return XError Object + /// + /// + /// + /// + public static XError AddContentToError (this XException exception, string value) { + // + var xError = exception.ToXError (); + xError.Message = string.Format (xError.Message, value); + + // + return xError; + } + + /// + /// Add Content to Contentable XException member and return Exception Object + /// + /// + /// + /// + public static Exception AddContentToException (this XException exception, string value) { + // + var xError = exception.ToXError (); + xError.Message = string.Format (xError.Message, value); + + // + return xError.ToException (); + } + + /// + /// Throw Specific Exception ... + /// + /// + /// + public static void Throw ( + this XException source, + string content = null + ) { + // + Exception exception = null; + if (content.IsNullOrEmpty ()) { + exception = source.ToException (); + } else { + exception = source.AddContentToException (content); + } + + // + throw exception; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\FilterExtensions.cs + +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.AspNetCore.Mvc.Filters; +using xCommons.Configurations; + +namespace xCommons.Extensions { + public static partial class FilterExtensions { + public static XAppConfiguration GetXAppConfiguration (this ActionExecutingContext source) { + // + var services = source.HttpContext.RequestServices; + return (XAppConfiguration) services.GetService (typeof (XAppConfiguration)); + } + + public static XAppConfiguration GetXAppConfiguration (this AuthorizationFilterContext source) { + // + var services = source.HttpContext.RequestServices; + return (XAppConfiguration) services.GetService (typeof (XAppConfiguration)); + } + + public static bool IsAnonymousAllowed (this AuthorizationFilterContext source) { + // + var result = source.Filters + .Any (f => f.GetType () == typeof (AllowAnonymousFilter)) || + source.ActionDescriptor.FilterDescriptors + .Any (f => f.GetType () == typeof (AllowAnonymousFilter)) || + source.ActionDescriptor.EndpointMetadata + .Any (f => f.GetType () == typeof (AllowAnonymousAttribute)); + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\IConfigurationExtensions.cs + +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; +using xCommons.Configurations; +using xCommons.Constants; + +namespace xCommons.Extensions +{ + public static partial class IConfigurationExtensions + { + /// + /// Retrieve AppConfiguration from Configurations + /// + /// + /// + public static XAppConfiguration GetXAppConfiguration(this IConfiguration source) + { + // + var xMessageResourceTitlesConfigSection = source + .GetSection(ConfigurationNodeNames.MESSAGE_RESOURCE_TITLES_NODE); + + // + var result = new XAppConfiguration + { + Name = source[nameof(XAppConfiguration.Name)], + Version = source[nameof(XAppConfiguration.Version)], + XPoweredValue = source[nameof(XAppConfiguration.XPoweredValue)], + WelcomeMessage = source[nameof(XAppConfiguration.WelcomeMessage)], + AllowedOrigins = source + .GetSection(nameof(XAppConfiguration.AllowedOrigins)) + .Get>(), + DefaultLanguage = source[nameof(XAppConfiguration.DefaultLanguage)], + MessageResourceTitles = xMessageResourceTitlesConfigSection + .Get() + }; + + // + return result; + } + + /// + /// Retrieve Swagger Configurations + /// + /// + /// + public static XSwaggerConfiguration GetXSwaggerConfiguration(this IConfiguration source) + { + // + var xSwaggerConfigSection = source + .GetSection(ConfigurationNodeNames.SWAGGER_NODE); + return xSwaggerConfigSection.Get(); + } + + /// + /// Converts a ConfigurationSection to object Representation ... + /// + /// + /// + public static object ToObject(this IConfigurationSection section) + { + // + // an Inner Function for Create Recursive Parsing ... + object ConvertSection(IConfigurationSection sec) + { + // + var children = sec + .GetChildren() + .ToList(); + if (!children.Any()) + { + return sec.Value ?? string.Empty; + } + + // + // Detect Array Like Sections ... + bool isArray = children.All(ch => int.TryParse(ch.Key, out _)); + if (isArray) + { + // + return children + .OrderBy(c => int.Parse(c.Key)) + .Select(ConvertSection) + .ToList(); + } + + // + // Otherwise ... + return children + .ToDictionary( + c => c.Key, + c => ConvertSection(c) + ); + } + + // + // Try to Parse using Inner Function ... + var result = ConvertSection(section); + + // + return result; + } + + /// + /// Converts a ConfigurationSection to Json Representation ... + /// + /// + /// + public static string ToJSON(this IConfigurationSection section) + { + // + // Try to Parse using Inner Function ... + var obj = section.ToObject(); + + // + // Convert it to Json ... + var result = obj.ToJSON(); + + // + return result; + } + + /// + /// Get a Configuration Section as Object ... + /// + /// + /// + /// + public static object GetSectionAsObject( + this IConfiguration source, + string section + ) + { + // + object result = null; + + // + if (!section.IsNullOrEmpty()) + { + // + var sec = source.GetSection(section); + if (!sec.IsNull()) + { + result = sec.ToObject(); + } + } + + // + return result; + } + + /// + /// Retrieve a Configuration Section as Json String ... + /// + /// + /// + /// + public static string GetSectionAsJson( + this IConfiguration source, + string section + ) + { + // + var result = string.Empty; + + // + // Retrieve Secion as Object ... + var obj = source.GetSectionAsObject(section); + if (!obj.IsNull()) + { + // + // Converts Section Object to Json ... + result = obj.ToJSON(); + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\IFormFileExtensions.cs + +using System; +using System.IO; +using System.Linq; +using System.Text; +using UglyToad.PdfPig; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using xCommons.Extensions; + +namespace xAiModels.Extensions +{ + public static class IFormFileExtensions + { + /// + /// Read a IFormFile Content as byte array ... + /// + /// + /// + /// + public static async Task ReadAsBytes( + this IFormFile source, + CancellationToken cancellationToken = default + ) + { + // + // Create and use a Memory Stream ... + using var stream = new MemoryStream(); + + // + // Copy File Content to Stream ... + await source.CopyToAsync( + target: stream, + cancellationToken: cancellationToken + ); + + // + // Read Stream Content as byte array ... + var result = stream.ToArray(); + + // + return result; + } + + /// + /// Read a File Content by it's Path as byte array ... + /// + /// + /// + /// + /// + /// + public static async Task ReadAsBytes( + this string filePath, + int bufferSize = 81920, + CancellationToken cancellationToken = default + ) + { + // + if (string.IsNullOrWhiteSpace(filePath)) + { + throw new Exception("Invalid Args ..."); + } + + // + // Create and use a Memory Stream ... + using var memStream = new MemoryStream(); + + // + using var stream = new FileStream( + path: filePath, + mode: FileMode.Open, + access: FileAccess.Read, + share: FileShare.ReadWrite + ); + + // + // Copy File Content to Stream ... + await stream.CopyToAsync( + destination: memStream, + bufferSize: bufferSize, + cancellationToken: cancellationToken + ); + + // + // Read Stream Content as byte array ... + var result = memStream.ToArray(); + + // + return result; + } + + /// + /// Read Text Content of Document ... + /// + /// + /// + /// + public static async Task ReadContent( + this IFormFile source, + CancellationToken cancellationToken = default + ) + { + // + // Check if File PDF or not ... + var isPDF = source.ContentType + .ToNormalString() + .Contains("pdf" + .ToNormalString()); + + // + var result = string.Empty; + + // + var isValidFileType = isPDF; + if (!isValidFileType) + { + return result; + } + + // + if (isPDF) + { + // + // Reading File Content as Byte array ... + var fileBytes = await source.ReadAsBytes(cancellationToken); + + // + // Create a PDF Document ... + using var stream = new MemoryStream(fileBytes); + using var document = PdfDocument.Open(stream); + + // + // Extract PDF Text ... + var textBuilder = new StringBuilder(); + foreach (var page in document.GetPages()) + { + textBuilder.Append(page.Text); + } + + // + result = textBuilder.ToString(); + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\ModelExtensions.cs + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace xCommons.Extensions +{ + public static partial class ModelExtensions + { + /// + /// Retrieve a Binding Flag for Retrieve all Instance Members including + /// Priate and Public ... + /// + /// + public static BindingFlags GetDefaultBindingFlag() + { + return BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + } + + /// + /// Update an Instance of a Typed Object with Data Provided by another Instance of Typed Object + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static T UpdateData( + this T source, + D updateWith, + ICollection propertyWhiteList = null, + ICollection propertyBlackList = null, + ICollection>> propertyValueProviders = null, + bool updateWithNullOrEmptyValues = false, + bool throwExceptionOnFails = false + ) + where T : class + where D : class + { + // + var hasWhiteList = propertyWhiteList.HasChild(); + var hasBlackList = propertyBlackList.HasChild(); + var hasValueProvider = propertyValueProviders.HasChild>>(); + + // + var fullPropertyList = updateWith.GetType().GetProperties().Select(prop => prop.Name); + var mustSetProps = (hasWhiteList ? propertyWhiteList : fullPropertyList); + + // + if (hasBlackList) + { + mustSetProps = mustSetProps.Where(propName => !propertyBlackList.Contains(propName)); + } + + // + mustSetProps.ToList().ForEach(propName => + { + // + try + { + // + var sourceProp = source.GetType().GetProperty(propName); + var sourceValue = sourceProp.GetValue(source); + var sourceValueType = sourceValue.IsNull() ? null : sourceValue.GetType(); + + // + var updateWithProp = updateWith.GetType().GetProperty(propName); + var updateWithValue = updateWithProp.GetValue(updateWith); + var updateWithValueType = updateWithValue.IsNull() ? null : updateWithValue.GetType(); + + // + var valueProviderValue = hasValueProvider ? propertyValueProviders + .FirstOrDefault(p => p.Key == propName).Value : null; + var updateVal = !valueProviderValue.IsNull() ? + valueProviderValue.Invoke(updateWith) : + (updateWithValue.IsNull() && !updateWithNullOrEmptyValues) ? + sourceValue : updateWithValue; + var updateValType = updateVal.IsNull() ? null : updateVal.GetType(); + + // + Type t = Nullable.GetUnderlyingType(sourceProp.PropertyType) ?? sourceProp.PropertyType; + object safeValue = + (updateVal == null) ? + null : + !updateValType.IsCollectionType() ? + Convert.ChangeType(updateVal, t) : + updateVal; + + // + sourceProp.SetValue(source, safeValue); + } + catch (Exception ex) + { + // + if (throwExceptionOnFails) + { + throw ex; + } + } + }); + + // + return source; + } + + /// + /// Check a Class is Contains a Property or not ... + /// + /// + /// + /// + /// + /// + public static bool HasProperty( + this T source, + string property, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + where T : class + { + // + var result = source + .GetType() + .GetProperties(bindingAttr) + .Any(prop => prop.Name == property); ; + + // + return result; + } + + /// + /// Check a Class is Contains a field or not ... + /// + /// + /// + /// + /// + /// + public static bool HasField( + this T source, + string field, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + where T : class + { + // + var result = source + .GetType() + .GetFields(bindingAttr) + .Any(fld => fld.Name == field); + + // + return result; + } + + /// + /// Check a Class is Contains a Method or not ... + /// + /// + /// + /// + /// + /// + public static bool HasMethod( + this T source, + string method, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + where T : class + { + // + var result = source + .GetType() + .GetMethods(bindingAttr) + .Any(mthd => mthd.Name == method); + + // + return result; + } + + /// + /// Retrieve a Class Properties Names ... + /// + /// + /// + /// + /// + public static string[] GetPropertiesNames( + this T source, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + { + // + var result = source + .GetType() + .GetProperties(bindingAttr) + .Select(p => p.Name) + .ToArray(); + + // + return result; + } + + /// + /// Retrieve a Class Fields Names ... + /// + /// + /// + /// + /// + public static string[] GetFieldsNames( + this T source, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + { + // + var result = source + .GetType() + .GetFields(bindingAttr) + .Select(p => p.Name) + .ToArray(); + + // + return result; + } + + /// + /// Retrieve a Class Methods Names ... + /// + /// + /// + /// + /// + public static string[] GetMethodsNames( + this T source, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + { + // + var result = source + .GetType() + .GetMethods(bindingAttr) + .Select(p => p.Name) + .ToArray(); + + // + return result; + } + + /// + /// Retrieve a Class Members Names ... + /// + /// + /// + /// + /// + public static string[] GetMembersNames( + this T source, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + { + // + var result = source + .GetType() + .GetMembers(bindingAttr) + .Select(p => p.Name) + .ToArray(); + + // + return result; + } + + /// + /// Get a Class Property Value using Generic Concept ... + /// + /// + /// + /// + /// + /// + /// + public static TProp GetProperty( + this T source, + string property, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + where T : class + { + // + TProp result = default; + + // + // Check if T Has Property ... + if (source.HasProperty(property)) + { + // + // Access Property ... + var prop = source + .GetType() + .GetProperty( + property, + bindingAttr + ); + + // + // Retrieve Property Value as Object ... + var propVal = prop.GetValue(source); + + // + result = (TProp)propVal; + } + + // + return result; + } + + /// + /// Get a Class Field Value using Generic Concept ... + /// + /// + /// + /// + /// + /// + /// + public static TField GetField( + this T source, + string field, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + where T : class + { + // + TField result = default; + + // + if (source.HasField(field)) + { + // + var fld = source + .GetType() + .GetField( + field, + bindingAttr + ); + + // + var fieldVal = fld.GetValue(source); + result = (TField)fieldVal; + } + + // + return result; + } + + /// + /// Set a Class Property Value using Generic Concept ... + /// + /// + /// + /// + /// + /// + /// + /// + public static bool SetProperty( + this T source, + string property, + TProp value, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + where T : class + { + // + // Check T Has Property ... + var result = source.HasProperty(property); + if (!result) + { + return result; + } + + // + // Access Property ... + var prop = source + .GetType() + .GetProperty( + property, + bindingAttr + ); + + // + // Set Property ... + try + { + // + result = true; + prop.SetValue(source, value); + } + catch + { + result = false; + } + + // + return result; + } + + /// + /// Set a Class Field Value using Generic Concept ... + /// + /// + /// + /// + /// + /// + /// + /// + public static bool SetField( + this T source, + string field, + TField value, + BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic + ) + where T : class + { + // + // Check T Has Field ... + var result = source.HasField(field); + if (!result) + { + return result; + } + + // + // Access Field ... + var fld = source + .GetType() + .GetField( + field, + bindingAttr + ); + + // + // Set Value ... + try + { + // + result = true; + fld.SetValue(source, value); + } + catch + { + result = false; + } + + // + return result; + } + + /// + /// Check two Instance of Typed Object Values are Same or not + /// + /// + /// + /// + /// + /// + /// + /// /// + /// + /// + public static bool IsSameContent( + this T source, + D dest, + ICollection propertyWhiteList = null, + ICollection propertyBlackList = null, + ICollection>> propertyValueCheckers = null, + bool throwExceptionOnFails = false + ) + where T : class + where D : class + { + // + var hasWhiteList = propertyWhiteList.HasChild(); + var hasBlackList = propertyBlackList.HasChild(); + var hasCheckerProvider = propertyValueCheckers.HasChild>>(); + + // + var fullPropertyList = dest.GetType().GetProperties().Select(prop => prop.Name); + var mustCheckProps = (hasWhiteList ? propertyWhiteList : fullPropertyList); + if (hasBlackList) + { + mustCheckProps = mustCheckProps.Where(propName => !propertyBlackList.Contains(propName)); + } + + // + var result = true; + mustCheckProps.ToList().ForEach(propName => + { + // + try + { + // + var sourceProp = source.GetType().GetProperty(propName); + var destProp = dest.GetType().GetProperty(propName); + + // + // Check Types ... + var isSameType = true; + + // + // Check + var isSameValue = false; + var mustDoCustomCheck = hasCheckerProvider && propertyValueCheckers.Any(p => p.Key == propName); + if (mustDoCustomCheck) + { + // + // Check Values ... + var valueChecker = propertyValueCheckers.FirstOrDefault(p => p.Key == propName).Value; + isSameValue = valueChecker.Invoke(source, dest); + } + else + { + // // + // isSameType = (sourceProp.PropertyType == destProp.PropertyType && + // sourceProp.ReflectedType == destProp.ReflectedType); + + // + // Check Values ... + isSameValue = Equals(sourceProp.GetValue(source), destProp.GetValue(dest)); + } + + // + if (!isSameType || !isSameValue) + { + result = false; + } + } + catch (Exception ex) + { + // + if (throwExceptionOnFails) + { + throw ex; + } + } + }); + + // + return result; + } + + /// + /// Check specific type is Collection Type or not + /// + /// + /// + public static bool IsCollectionType(this Type source) + { + // + if (source.IsNull() || !source.IsGenericType) + { + return false; + } + + // + var genericTypeDefinition = source.GetGenericTypeDefinition(); + var result = genericTypeDefinition == typeof(List<>) || + genericTypeDefinition == typeof(HashSet<>) || + genericTypeDefinition == typeof(Collection<>) || + genericTypeDefinition == typeof(ICollection<>) || + genericTypeDefinition == typeof(IEnumerable<>); + + // + return result; + } + + /// + /// Retrieve a Property Value in Generic + /// + /// + /// + /// + public static string GetPropValues(this T item) + where T : class + { + // + var props = item.GetType().GetProperties(); + var vals = props.Select(p => p.GetValue(item)); + + // + return vals.ToJSON(); + } + + /// + /// Check a Property Value Contains specific value + /// + /// + /// + /// + /// + public static bool PropValuesContains(this T item, string value) + where T : class + { + // + return item.GetPropValues() + .ToNormalString() + .Contains(value + .ToNormalString()); + } + + /// + /// Generate Default Column Map + /// + /// + /// + /// + public static IDictionary>> GetDefaultColumnsMap(this T item) + where T : class + { + // + if (item.IsNull()) + { + return null; + } + + // + var result = new Dictionary>>(); + var props = item.GetType().GetProperties(); + + // + foreach (var prop in props) + { + result.Add(prop.Name, t => t.GetType().GetProperty(prop.Name).GetValue(t, null)); + } + + // + return result; + } + + /// + /// Generate Default Column Map + /// + /// + /// + /// + public static IDictionary GetPropValuesDictionary(this T item) + where T : class + { + // + if (item.IsNull()) + { + return null; + } + + // + var result = new Dictionary(); + var props = item.GetType().GetProperties(); + + // + foreach (var prop in props) + { + result.Add(prop.Name, prop.GetValue(item).ToJSON()); + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\ServiceProviderExtensions.cs + +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Helpers; + +namespace xCommons.Extensions { + public static class ServiceProviderExtensions { + /// + /// Get all registered + /// + /// + /// + public static Dictionary GetAllServiceDescriptors (this IServiceProvider provider) { + // + if (provider is ServiceProvider serviceProvider) { + // + var result = new Dictionary (); + + // + var engine = serviceProvider.GetFieldValue ("_engine"); + var callSiteFactory = engine.GetPropertyValue ("CallSiteFactory"); + var descriptorLookup = callSiteFactory.GetFieldValue ("_descriptorLookup"); + if (descriptorLookup is IDictionary dictionary) { + foreach (DictionaryEntry entry in dictionary) { + result.Add ((Type) entry.Key, (ServiceDescriptor) entry.Value.GetPropertyValue ("Last")); + } + } + + // + return result; + } + + // + throw new NotSupportedException ($"Type '{provider.GetType()}' is not supported!"); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\XHttpContextExtensions.cs + +using System.IO; +using Microsoft.AspNetCore.Http; + +namespace xCommons.Extensions { + public static partial class XHttpContextExtensions { + /// + /// Convert IFormFile to Bytes Array + /// + /// + /// + public static byte[] ToByteArray (this IFormFile source) { + // + byte[] data; + using (var br = new BinaryReader (source.OpenReadStream ())) { + data = br.ReadBytes ((int) source.OpenReadStream ().Length); + } + + // + return data; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Extensions\XMiddlewareExtensions.cs + +using System; +using Microsoft.AspNetCore.Builder; +using xCommons.Middlewares; + +namespace xCommons.Extensions +{ + public static class XMiddlewareExtensions + { + /// + /// Use XRequestLogger Middleware for log Requests ... + /// + /// + public static void UseXRequestLogger(this IApplicationBuilder app) { + app.UseMiddleware(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Filters\RequireXPoweredOperationFilter.cs + +using System.Collections.Generic; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using xCommons.Configurations; +using xCommons.Constants; +using xCommons.Extensions; + +namespace xCommons.Filters { + /// + /// RequireXPowered for NGSwag + /// + public partial class RequireXPoweredOperationFilter : IOperationFilter { + private readonly XAppConfiguration config; + + public RequireXPoweredOperationFilter (XAppConfiguration config) { + this.config = config; + } + + public void Apply (OpenApiOperation operation, OperationFilterContext context) { + // + if (operation.Parameters.IsNull ()) { + operation.Parameters = new List (); + } + + // + operation.Parameters.Add (new OpenApiParameter { + In = ParameterLocation.Header, + Name = XAuthorization.XPoweredBy, + Description = "a Basic Authorization Filter", + Required = false, + Schema = new OpenApiSchema { + Type = "String", + Default = new OpenApiString (config.XPoweredValue) + } + }); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Filters\SwaggerDefaultValuesOperationFilter.cs + +using System.Linq; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using xCommons.Configurations; +using xCommons.Extensions; + +namespace xCommons.Filters +{ + public class SwaggerDefaultValuesOperationFilter : IOperationFilter + { + private readonly XSwaggerConfiguration config; + + public SwaggerDefaultValuesOperationFilter( + XSwaggerConfiguration config + ) + { + this.config = config; + } + + public void Apply( + OpenApiOperation operation, + OperationFilterContext context + ) + { + // + // Validate Configuration ... + if ( + !config.IsNull() && + !config.DefaultValues.IsNull() && + config.DefaultValues.HasChild() + ) + { + // + foreach (var prv in config.DefaultValues) + { + // + // Detect Parameter ... + var swaggerParam = operation.Parameters.FirstOrDefault(p => p.Name == prv.Key); + if (!swaggerParam.IsNull()) + { + swaggerParam.Schema.Default = new OpenApiString(prv.Value); + } + } + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Helpers\ObjectHelper.cs + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace xCommons.Helpers { + public partial class ObjectHelper { + /// + /// Convert Enum Keys to Array + /// + /// + /// + public static Array GetKeys () { + // + ValidateEnumbType (); + var type = typeof (T); + + // + var result = Enum.GetNames (type); + + // + return result; + } + + /// + /// Convert EnumValues to Array + /// + /// + /// + public static Array GetValues () { + // + ValidateEnumbType (); + var type = typeof (T); + + // + var result = Enum.GetValues (type); + + // + return result; + } + + /// + /// Convert an Enum to IEnumerable of it's Childs + /// + /// + /// + public static IEnumerable ToEnumerableValues () { + // + ValidateEnumbType (); + var type = typeof (T); + + // + var result = GetValues () + .Cast (); + + // + return result; + } + + /// + /// Retrieve Keys of an Enumerable + /// + /// + /// + public static IEnumerable ToEnumerableKeys () { + // + ValidateEnumbType (); + var type = typeof (T); + + // + var result = GetKeys () + .Cast (); + + // + return result; + } + + public static IDictionary ToDictionary () { + // + ValidateEnumbType (); + + // + var result = new Dictionary (); + foreach (var name in Enum.GetNames (typeof (T))) { + result.Add (name, (int) Enum.Parse (typeof (T), name)); + } + + // + return result; + } + + private static void ValidateEnumbType () { + // + var type = typeof (T); + if (!type.IsEnum) { + throw new InvalidCastException (); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Helpers\XDateHelper.cs + +using System; + +namespace xCommons.Helpers { + public partial class XDateHelper { + /// + /// retrieve utc now timestam unix like representation ... + /// + /// + public static long UtcNowTimespan () { + return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds (); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Helpers\XGenericHelper.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using xCommons.Extensions; + +namespace xCommons.Helpers +{ + public static class XGenericHelper + { + /// + /// Invoke a Generic Method of Specified Type ... + /// + /// + /// + /// + /// + public static void InvokeGenericMethod( + this Type source, + string methodName, + Type runtimeType, + params object[] args + ) + { + // + source.InvokeGenericMethod( + args: args, + methodName: methodName, + runtimeType: runtimeType + ); + } + + /// + /// Invoke a Generic Method of Specified Type ... + /// + /// + /// + /// + /// + /// + /// + public static TResponse InvokeGenericMethod( + this Type source, + string methodName, + Type runtimeType, + params object[] args + ) + { + // + var overLoads = source.GetGenericMethodInfo( + methodName: methodName + ); + if (!overLoads.HasChild()) + { + return default; + } + + // + var method = overLoads.ElementAt(0); + MethodInfo genericMethod = method.MakeGenericMethod(runtimeType); + + // + var resultObj = genericMethod.Invoke(null, args); + var result = (TResponse)resultObj; + + // + return result; + } + + /// + /// Invoke a Generic Method of Specified Type ... + /// + /// + /// + /// + /// + public static void InvokeGenericMethod( + this Type source, + string methodName, + Type[] runtimeTypes, + params object[] args + ) + { + // + source.InvokeGenericMethod( + args: args, + methodName: methodName, + runtimeTypes: runtimeTypes + ); + } + + /// + /// Invoke a Generic Method of Specified Type ... + /// + /// + /// + /// + /// + /// + /// + public static TResponse InvokeGenericMethod( + this Type source, + string methodName, + Type[] runtimeTypes, + params object[] args + ) + { + // + var overLoads = source.GetGenericMethodInfo( + methodName: methodName + ); + if (!overLoads.HasChild()) + { + return default; + } + + // + // Detect a Method by Generic Types ... + // var method = overLoads.ElementAt(0); + var method = overLoads.First(m => m.GetGenericArguments().Length == runtimeTypes.Length); + MethodInfo genericMethod = method.MakeGenericMethod(runtimeTypes); + + // + var resultObj = genericMethod.Invoke(null, args); + var result = resultObj.ConvertTo(); + + // + return result; + } + + /// + /// Extract a Generic Method's Overloads ... + /// + /// + /// + /// + public static List GetGenericMethodInfo( + this Type source, + string methodName + ) + { + // + var methods = source + .GetMethods(); + + // + var result = methods + .Where(mi => + mi.Name == methodName && + mi.IsGenericMethodDefinition) + .ToList(); + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Helpers\XMLToJSONHelper.cs + +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace xCommons.Helpers { + public partial class XMLToJSONHelper { + // + #region Actions ... + public static string ToJSON (string xml) { + // + XmlDocument doc = new XmlDocument (); + doc.LoadXml (xml); + + // + return XmlToJSON (doc); + } + + public static string XmlToJSON (XmlDocument xmlDoc) { + // + StringBuilder sbJSON = new StringBuilder (); + + // + sbJSON.Append ("{ "); + XmlToJSONnode (sbJSON, xmlDoc.DocumentElement, true); + sbJSON.Append ("}"); + + // + return sbJSON.ToString (); + } + #endregion + + // + #region Private ... + // + private static void XmlToJSONnode (StringBuilder sbJSON, XmlElement node, bool showNodeName) { + // + if (showNodeName) { + sbJSON.Append ("\"" + SafeJSON (node.Name) + "\": "); + } + + // + sbJSON.Append ("{"); + + // + // Build a sorted list of key-value pairs + // where key is case-sensitive nodeName + // value is an ArrayList of string or XmlElement + // so that we know whether the nodeName is an array or not. + SortedList childNodeNames = new SortedList (); + + // + // Add in all node attributes + if (node.Attributes != null) + foreach (XmlAttribute attr in node.Attributes) + StoreChildNode (childNodeNames, attr.Name, attr.InnerText); + + // + // Add in all nodes + foreach (XmlNode cnode in node.ChildNodes) { + // + if (cnode is XmlText) { + StoreChildNode (childNodeNames, "value", cnode.InnerText); + } else if (cnode is XmlElement) { + StoreChildNode (childNodeNames, cnode.Name, cnode); + } + } + + // + // Now output all stored info + foreach (string childname in childNodeNames.Keys) { + // + List alChild = (List) childNodeNames[childname]; + if (alChild.Count == 1) { + OutputNode (childname, alChild[0], sbJSON, true); + } else { + // + sbJSON.Append (" \"" + SafeJSON (childname) + "\": [ "); + + // + foreach (object Child in alChild) { + OutputNode (childname, Child, sbJSON, false); + } + + // + sbJSON.Remove (sbJSON.Length - 2, 2); + sbJSON.Append (" ], "); + } + } + + // + sbJSON.Remove (sbJSON.Length - 2, 2); + sbJSON.Append (" }"); + } + + // + // StoreChildNode: Store data associated with each nodeName + // so that we know whether the nodeName is an array or not. + private static void StoreChildNode (SortedList childNodeNames, string nodeName, object nodeValue) { + // + // Pre-process contraction of XmlElement-s + if (nodeValue is XmlElement) { + // + // Convert into "aa":null + // xx into "aa":"xx" + XmlNode cnode = (XmlNode) nodeValue; + if (cnode.Attributes.Count == 0) { + // + XmlNodeList children = cnode.ChildNodes; + if (children.Count == 0) { + nodeValue = null; + } else if (children.Count == 1 && (children[0] is XmlText)) { + nodeValue = ((XmlText) (children[0])).InnerText; + } + } + } + + // + // Add nodeValue to ArrayList associated with each nodeName + // If nodeName doesn't exist then add it + List ValuesAL; + + // + if (childNodeNames.ContainsKey (nodeName)) { + ValuesAL = (List) childNodeNames[nodeName]; + } else { + // + ValuesAL = new List (); + childNodeNames[nodeName] = ValuesAL; + } + + // + ValuesAL.Add (nodeValue); + } + + // + private static void OutputNode (string childname, object alChild, StringBuilder sbJSON, bool showNodeName) { + if (alChild == null) { + if (showNodeName) + sbJSON.Append ("\"" + SafeJSON (childname) + "\": "); + sbJSON.Append ("null"); + } else if (alChild is string) { + if (showNodeName) + sbJSON.Append ("\"" + SafeJSON (childname) + "\": "); + string sChild = (string) alChild; + sChild = sChild.Trim (); + sbJSON.Append ("\"" + SafeJSON (sChild) + "\""); + } else + XmlToJSONnode (sbJSON, (XmlElement) alChild, showNodeName); + sbJSON.Append (", "); + } + + // + // Make a string safe for JSON + private static string SafeJSON (string sIn) { + // + StringBuilder sbOut = new StringBuilder (sIn.Length); + foreach (char ch in sIn) { + // + if (Char.IsControl (ch) || ch == '\'') { + // + int ich = (int) ch; + sbOut.Append (@"\u" + ich.ToString ("x4")); + continue; + } else if (ch == '\"' || ch == '\\' || ch == '/') { + sbOut.Append ('\\'); + } + + // + sbOut.Append (ch); + } + + // + return sbOut.ToString (); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Helpers\XNetworkHelper.cs + +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using xCommons.Models; + +namespace xCommons.Helpers { + public static class XNetworkHelper { + /// + /// Retrieve Application Host's Machin IP Addresses ... + /// + /// string + public static string GetIp () { + // + var name = Dns.GetHostName (); // get container id + var result = Dns.GetHostEntry (name) + .AddressList + .FirstOrDefault ( + x => x.AddressFamily == AddressFamily.InterNetwork + ) + .MapToIPv4 () + .ToString (); + + // + return result; + } + + /// + /// Retrieve All Available IP's in Application Host's Machin ... + /// + /// a Collection of XNetworkInfo class instances + public static IEnumerable GetNetworkInfos () { + // + var result = System.Net.NetworkInformation.NetworkInterface + .GetAllNetworkInterfaces () + .Where ( + ni => + !ni.IsReceiveOnly && + ni.OperationalStatus == OperationalStatus.Up && + ni.NetworkInterfaceType != NetworkInterfaceType.Loopback + ) + .SelectMany (ni => + ni + .GetIPProperties () + .GatewayAddresses + .Select (ga => + new { + Name = ni.Name, + Description = ni.Description, + Gateway = ga.Address + .MapToIPv4 () + .ToString (), + NI = ni + } + ) + ) + .SelectMany (data => + data.NI + .GetIPProperties () + .UnicastAddresses + .Select (ip => + new XNetworkInfo { + Name = data.Name, + Gateway = data.Gateway, + Description = data.Description, + NetworkInterface = data.NI, + IP = ip.Address + .MapToIPv4 () + .ToString () + } + ) + ); + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Helpers\XOsHelper.cs + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Attributes; +using xExceptions.Constants; + +namespace xCommons.Helpers +{ + /// + /// Several OS Type ... + /// + public enum XOSType + { + [StringValue("Unknown")] + XUnknown, + + [StringValue("Mac")] + XMacOs, + + [StringValue("Linux")] + XLinux, + + [StringValue("Windows")] + XWindows, + + [StringValue("FreeBSD")] + XFreeBSD, + } + + /// + /// OS Command Execution Result ... + /// + public class XOSCMDResult + { + /// + /// Execution Result ... + /// + /// + public string Result { get; set; } + + /// + /// Execution Error ... + /// + /// + public string Error { get; set; } + } + + /// + /// a Helper Class which Provides OS Command Executions ... + /// + public static partial class XOsHelper + { + /// + /// Retrieve OS Type ... + /// + /// + public static XOSType GetOSType() + { + // + XOSType result = XOSType.XUnknown; + + // + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + result = XOSType.XMacOs; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + result = XOSType.XLinux; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + result = XOSType.XWindows; + } + // else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + // { + // result = XOSType.XFreeBSD; + // } + + // + return result; + } + + /// + /// Retrieve OS Description ... + /// + /// + public static string GetOSDescription() + { + // + var result = RuntimeInformation.OSDescription; + return result; + } + + /// + /// Execute an Specified Command ... + /// + /// Specified Command + /// Command Arguments + /// + public static async Task Execute( + string cmd, + string args + ) + { + // + var result = new XOSCMDResult + { + Error = "Unknown", + }; + + // + try + { + // + // Validate CMD ... + var isValid = !cmd.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Validate OS Type ... + XOSType osType = GetOSType(); + isValid = osType != XOSType.XUnknown; + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + return await Task.Run(() => + { + // + ProcessStartInfo psi = new ProcessStartInfo(); + + // + psi.FileName = cmd; + psi.Arguments = args; + psi.UseShellExecute = false; + psi.RedirectStandardError = true; + psi.RedirectStandardOutput = true; + + // + Process process = Process.Start(psi); + + // + // Read the output + string output = process.StandardOutput.ReadToEnd(); + string error = process.StandardError.ReadToEnd(); + + // + process.WaitForExit(); + + // + result = new XOSCMDResult + { + Error = error, + Result = output, + }; + + // + return result; + }); + } + catch (Exception ex) + { + result.Error = ex.Message; + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Helpers\XReflectionHelper.cs + +using System; +using System.Reflection; + +namespace xCommons.Helpers { + public static class XReflectionHelper { + public static object GetFieldValue (this object obj, string fieldName) { + if (obj == null) + throw new ArgumentNullException (nameof (obj)); + Type objType = obj.GetType (); + var fieldInfo = GetFieldInfo (objType, fieldName); + if (fieldInfo == null) + throw new ArgumentOutOfRangeException (fieldName, + $"Couldn't find field {fieldName} in type {objType.FullName}"); + return fieldInfo.GetValue (obj); + } + + public static void SetFieldValue (this object obj, string fieldName, object val) { + if (obj == null) + throw new ArgumentNullException (nameof (obj)); + Type objType = obj.GetType (); + var fieldInfo = GetFieldInfo (objType, fieldName); + if (fieldInfo == null) + throw new ArgumentOutOfRangeException (fieldName, + $"Couldn't find field {fieldName} in type {objType.FullName}"); + fieldInfo.SetValue (obj, val); + } + + private static FieldInfo GetFieldInfo (Type type, string fieldName) { + FieldInfo fieldInfo = null; + do { + fieldInfo = type.GetField (fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + type = type.BaseType; + } while (fieldInfo == null && type != null); + + return fieldInfo; + } + + public static object GetPropertyValue (this object obj, string propertyName) { + if (obj == null) + throw new ArgumentNullException (nameof (obj)); + Type objType = obj.GetType (); + var propertyInfo = GetPropertyInfo (objType, propertyName); + if (propertyInfo == null) + throw new ArgumentOutOfRangeException (propertyName, + $"Couldn't find property {propertyName} in type {objType.FullName}"); + return propertyInfo.GetValue (obj, null); + } + + public static void SetPropertyValue (this object obj, string propertyName, object val) { + if (obj == null) + throw new ArgumentNullException (nameof (obj)); + Type objType = obj.GetType (); + var propertyInfo = GetPropertyInfo (objType, propertyName); + if (propertyInfo == null) + throw new ArgumentOutOfRangeException (propertyName, + $"Couldn't find property {propertyName} in type {objType.FullName}"); + propertyInfo.SetValue (obj, val, null); + } + + private static PropertyInfo GetPropertyInfo (Type type, string propertyName) { + PropertyInfo propertyInfo = null; + do { + propertyInfo = type.GetProperty (propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + type = type.BaseType; + } while (propertyInfo == null && type != null); + + return propertyInfo; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Interfaces\IXCipher.cs + +namespace xCommons.Interfaces +{ + public interface IXCipher + { + string Encrypt(string plainText); + string Decrypt(string cipherText); + string EncryptFromBytes(byte[] textBytes); + string DecryptFromBytes(byte[] cipherBytes); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Middlewares\XRequestLogger.cs + +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xCommons.Models; + +namespace xCommons.Middlewares { + public partial class XRequestLogger { + + private readonly ILogger logger; + private readonly RequestDelegate next; + + public XRequestLogger ( + RequestDelegate next, + ILoggerFactory loggerFactory + ) { + // + this.next = next; + this.logger = loggerFactory + .CreateLogger (); + } + + public async Task InvokeAsync (HttpContext context) { + // + var logModel = new XLoggerModel (); + + // + logModel.Path = context.Request.Path; + logModel.Schema = context.Request.Scheme; + logModel.Method = context.Request.Method; + logModel.Headers = context.Request.Headers; + logModel.Host = context.Request.Host.ToString (); + logModel.QueryString = context.Request.QueryString.ToString (); + logModel.UserID = context.User.Identity.IsAuthenticated ? context.User.Identity.Name : "Not Authenticated"; + + // + logModel.Connection = new XLoggerHttpConnection { + Id = context.Connection.Id, + RemoteIpAddress = context.Connection.RemoteIpAddress.ToString (), + RemotePort = context.Connection.RemotePort.ToString (), + LocalIpAddress = context.Connection.LocalIpAddress.ToString (), + LocalPort = context.Connection.LocalPort.ToString (), + }; + + // + // Check if a request is a Post Call so read data from Body ... + if (context.Request.Method.ToNormalString () == "post") { + // + context.Request.EnableBuffering (); + var request = await new StreamReader (context.Request.Body) + .ReadToEndAsync (); + + // + context.Request.Body.Position = 0; + logModel.Request = request; + } + + // + logModel.RequestedOn = DateTime.UtcNow; + + // + // Do Request ... + await next.Invoke (context); + + // + using (Stream originalRequest = context.Response.Body) { + try { + using (var memStream = new MemoryStream ()) { + // + context.Response.Body = memStream; + + // + // All the Request processing as described above + // happens from here. + // Response handling starts from here + // set the pointer to the beginning of the + // memory stream to read + memStream.Position = 0; + + // + // read the memory stream till the end + var response = await new StreamReader (memStream) + .ReadToEndAsync (); + + // + logModel.Response = response; + logModel.RespondedOn = DateTime.UtcNow; + logModel.StatusCode = context.Response.StatusCode; + logModel.IsSucceed = context.Response.StatusCode >= 200 && context.Response.StatusCode <= 299; + + // + // since we have read till the end of the stream, + // reset it onto the first position + memStream.Position = 0; + + // + // now copy the content of the temporary memory + // stream we have passed to the actual response body + // which will carry the response out. + await memStream.CopyToAsync (originalRequest); + } + } catch (Exception ex) { + // + // Log Exception ... + logger.LogError ($"Error on Logging Request/Response ... {ex.ToJSON()}"); + } finally { + // + // assign the response body to the actual context + context.Response.Body = originalRequest; + } + } + + // + // Log Response ... + logger.LogCritical ($"Http Response/Response Information:{Environment.NewLine}" + + $"Schema:{logModel.Schema} " + Environment.NewLine + + $"Host: {logModel.Host} " + Environment.NewLine + + $"Path: {logModel.Path} " + Environment.NewLine + + $"Method: {logModel.Method} " + Environment.NewLine + + $"UserID: {logModel.UserID} " + Environment.NewLine + + $"QueryString: {context.Request.QueryString} " + Environment.NewLine + + $"Headers: {logModel.Headers.ToJSON()}" + Environment.NewLine + + $"" + Environment.NewLine + + $"Http Connection: " + Environment.NewLine + + $"Id: {logModel.Connection.Id}" + Environment.NewLine + + $"RemoteIpAddress: {logModel.Connection.RemoteIpAddress}" + Environment.NewLine + + $"RemotePort: {logModel.Connection.RemotePort}" + Environment.NewLine + + $"LocalIpAddress: {logModel.Connection.LocalIpAddress}" + Environment.NewLine + + $"LocalPort: {logModel.Connection.LocalPort}" + Environment.NewLine + + $"" + Environment.NewLine + + $"Request Body: {logModel.Request}" + Environment.NewLine + + $"RequestedOn: {logModel.RequestedOn}" + Environment.NewLine + + $"" + Environment.NewLine + + $"IsSucceed: {logModel.IsSucceed}" + Environment.NewLine + + $"StatucCode: {logModel.StatusCode}" + Environment.NewLine + + $"Response Body: {logModel.Response}" + Environment.NewLine + + $"RespondedOn: {logModel.RespondedOn}" + Environment.NewLine + + $"" + Environment.NewLine + ); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Models\XCertificate.cs + +namespace xCommons.Models { + public partial class XCertificate { + public string Path { get; set; } + public string Secret { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Models\XLoggerModel.cs + +using System; +using Microsoft.AspNetCore.Http; + +namespace xCommons.Models { + public class XLoggerModel { + public string Schema { get; set; } + public string Host { get; set; } + public string Path { get; set; } + public string Method { get; set; } + public string UserID { get; set; } + public string QueryString { get; set; } + public IHeaderDictionary Headers { get; set; } + public string Request { get; set; } + public int StatusCode { get; set; } + public bool IsSucceed { get; set; } + public string Response { get; set; } + public DateTime RequestedOn { get; set; } + public DateTime RespondedOn { get; set; } + public XLoggerHttpConnection Connection { get; set; } + } + + public class XLoggerHttpConnection { + public string Id { get; set; } + public string RemoteIpAddress { get; set; } + public string RemotePort { get; set; } + public string LocalIpAddress { get; set; } + public string LocalPort { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Models\XNetworkInfo.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.NetworkInformation; +using System.Threading.Tasks; + +namespace xCommons.Models { + public class XNetworkInfo { + /// + /// Network Name ... + /// + /// + public string Name { get; set; } + + /// + /// Network Description ... + /// + /// + public string Description { get; set; } + + /// + /// IP Address which Exists on Current Network ... + /// + /// + public string IP { get; set; } + + /// + /// Current Networks Gateway Address ... + /// + /// + public string Gateway { get; set; } + + /// + /// Network Interface Object ... + /// + /// + public NetworkInterface NetworkInterface { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Models\XServiceDescriptor.cs + +using System; + +namespace xCommons.Models +{ + public class XServiceDescriptor + { + public Type Service { get; set; } + public Type Implementation { get; set; } + } + + public class XServiceDescriptor : XServiceDescriptor + { + public XServiceDescriptor() : base() + { + Service = typeof(TService); + Implementation = typeof(TImplmentation); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Providers\XCipher.cs + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using xCommons.Extensions; +using xCommons.Interfaces; + +namespace xCommons.Providers +{ + public class XCipher : IXCipher + { + // + #region Props ... + private readonly string secretKey; + private readonly byte[] iv; + private readonly byte[] key; + private readonly XValidationProvider validationProvider; + #endregion + + // + #region Constructor ... + public XCipher( + string cipherKey, + XValidationProvider validationProvider + ) + { + // + this.secretKey = cipherKey.ToMd5String(); + this.validationProvider = validationProvider; + + // + var ivBytes = new byte[16]; + var keyBytes = new byte[32]; + var secretBytes = Encoding.UTF8.GetBytes(this.secretKey); + + // + Array.Copy( + secretBytes, + ivBytes, + secretBytes.Length < ivBytes.Length ? + secretBytes.Length : + ivBytes.Length + ); + this.iv = ivBytes; + + // + Array.Copy( + secretBytes, + keyBytes, + secretBytes.Length < keyBytes.Length ? + secretBytes.Length : + keyBytes.Length + ); + this.key = keyBytes; + + // Encoding.UTF8.GetBytes (this.secretKey); + } + #endregion + + // + #region Actions ... + public string Encrypt(string plainText) + { + // + validationProvider.NotEmpty(plainText); + + // + var textBytes = Encoding.UTF8.GetBytes(plainText); + var result = EncryptFromBytes(textBytes); + + // + return result; + } + + public string Decrypt(string cipherText) + { + // + validationProvider.NotEmpty(cipherText); + + // + var cipherBytes = Convert.FromBase64String(cipherText); + var result = DecryptFromBytes(cipherBytes); + + // + return result; + } + + public string EncryptFromBytes(byte[] textBytes) + { + // + // Validate Args ... + validationProvider.NotNull(textBytes); + + // Declare the string used to hold + // the decrypted text. + byte[] encryptedBytes = null; + + // Create an RijndaelManaged object + // with the specified key and IV. + using (var rijAlg = new RijndaelManaged()) + { + //Settings + rijAlg.Mode = CipherMode.CBC; + rijAlg.Padding = PaddingMode.PKCS7; + // rijAlg.FeedbackSize = 128; + + rijAlg.Key = key; + rijAlg.IV = iv; + + // + using (var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV)) + { + encryptedBytes = encryptor.TransformFinalBlock(textBytes, 0, textBytes.Length); + } + } + + // + return Convert.ToBase64String(encryptedBytes); + } + + public string DecryptFromBytes(byte[] cipherBytes) + { + // + // Validate Args ... + validationProvider.NotNull(cipherBytes); + + // Declare the string used to hold + // the decrypted text. + string plaintext = null; + + // Create an RijndaelManaged object + // with the specified key and IV. + using (var rijAlg = new RijndaelManaged()) + { + //Settings + rijAlg.Mode = CipherMode.CBC; + rijAlg.Padding = PaddingMode.PKCS7; + // rijAlg.FeedbackSize = 128; + + rijAlg.Key = key; + rijAlg.IV = iv; + + // Create a decrytor to perform the stream transform. + var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); + + // Create the streams used for decryption. + using (var msDecrypt = new MemoryStream(cipherBytes)) + { + using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + { + using (var srDecrypt = new StreamReader(csDecrypt)) + { + // Read the decrypted bytes from the decrypting stream + // and place them in a string. + plaintext = srDecrypt.ReadToEnd(); + } + } + } + } + + // + return plaintext; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xCommons\Providers\XValidationProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Constants; + +namespace xCommons.Providers { + public partial class XValidationProvider { + private ICollection validationGroup; + + // + #region Simple Validators ... + /// + /// given objects must not null + /// + /// + public void NotNull ( + params object[] values) { + // + foreach (var o in values) { + // + if (o == null) { + XException.InvalidArgs.Throw (); + } + } + } + + /// + /// given strings must not empty or null + /// + /// + public void NotEmpty ( + params string[] values) { + // + foreach (var s in values) { + // + if (s.IsNullOrEmpty ()) { + XException.InvalidArgs.Throw (); + } + } + } + + /// + /// Check a numeric value is bigger than 0 + /// + /// + public void NotZero ( + params int[] values) { + // + foreach (var s in values) { + // + if (s == 0) { + XException.InvalidArgs.Throw (); + } + } + } + + /// + /// Check a numeric value is bigger than 0 + /// + /// + public void NotZero ( + params long[] values) { + // + foreach (var s in values) { + // + if (s == 0) { + XException.InvalidArgs.Throw (); + } + } + } + + /// + /// Check a numeric value is bigger than 0 + /// + /// + public void NotZero ( + params decimal[] values) { + // + foreach (var s in values) { + // + if (s == 0) { + XException.InvalidArgs.Throw (); + } + } + } + + /// + /// Check a Value is a Valid Guid ... + /// + /// + public void IsGuid (params string[] values) { + // + foreach (var s in values) { + // + if (!s.IsGuid () || s.IsDefaultGuid ()) { + XException.InvalidArgs.Throw (); + } + } + } + + /// + /// Check a Value is a Valid Guid ... + /// + /// + public void IsGuid (params Guid[] values) { + // + foreach (var s in values) { + // + if (s.IsNull () || s.IsDefaultGuid ()) { + XException.InvalidArgs.Throw (); + } + } + } + + /// + /// Check a Value is an MD5 ... + /// + /// + public void IsMD5 (params string[] values) { + // + foreach (var s in values) { + // + if (!s.IsMD5String ()) { + XException.InvalidArgs.Throw (); + } + } + } + + /// + /// non zero childs + /// + /// + public void NotZeroChilds ( + params IEnumerable[] values) { + // + foreach (var s in values) { + // + if (s == null || + s.Count () == 0) { + XException.InvalidArgs.Throw (); + } + } + } + + /// + /// non zero childs + /// + /// + public void NotZeroChilds ( + params ICollection[] values) { + // + foreach (var s in values) { + // + if (s == null || + s.Count == 0) { + XException.InvalidArgs.Throw (); + } + } + } + + /// + /// given string must be a valid URL ... + /// + /// + /// + public void Url ( + string url, + Exception exception = null + ) { + // + NotEmpty (url); + + // + if (exception == null) { + exception = XException.InavlidUrl.ToException (); + } + + // + if (!url.IsValidUrl ()) { + throw exception; + } + } + + /// + /// given string must be a valid Email Address ... + /// + /// + /// + public void EmailAddress ( + string emailAdress, + Exception exception = null + ) { + // + NotEmpty (emailAdress); + + // + if (exception == null) { + exception = XException.InvalidEmailAddress.ToException (); + } + + // + if (!emailAdress.IsValidEmail ()) { + throw exception; + } + } + + /// + /// given string must be a valid Mobile Number ... + /// + /// + /// + public void MobileNumber ( + string mobileNumber, + Exception exception = null + ) { + // + NotEmpty (mobileNumber); + + // + if (exception == null) { + exception = XException.InvalidMobileNumber.ToException (); + } + + // + if (!mobileNumber.IsValidMobileNumber ()) { + throw exception; + } + } + #endregion + + // + #region Group Validatos ... + public XValidationProvider GroupValidationBuilder () { + // + validationGroup = new List (); + + // + return this; + } + + public XValidationProvider AddNotEmpty (params string[] values) { + // + this.validationGroup.Add (() => + this.NotEmpty (values) + ); + + // + return this; + } + + public XValidationProvider AddNotNull (params object[] values) { + // + this.validationGroup.Add (() => + this.NotNull (values) + ); + + // + return this; + } + + public XValidationProvider AddNotZero (params int[] values) { + // + this.validationGroup.Add (() => + this.NotZero (values) + ); + + // + return this; + } + + public XValidationProvider AddNotZero (params long[] values) { + // + this.validationGroup.Add (() => + this.NotZero (values) + ); + + // + return this; + } + + public XValidationProvider AddNotZero (params decimal[] values) { + // + this.validationGroup.Add (() => + this.NotZero (values) + ); + + // + return this; + } + + public XValidationProvider AddIsGuid (params string[] values) { + // + this.validationGroup.Add (() => + this.IsGuid (values) + ); + + // + return this; + } + + public XValidationProvider AddIsGuid (params Guid[] values) { + // + this.validationGroup.Add (() => + this.IsGuid (values) + ); + + // + return this; + } + + public XValidationProvider AddIsMD5 (params string[] values) { + // + this.validationGroup.Add (() => + this.IsMD5 (values) + ); + + // + return this; + } + + public XValidationProvider AddNotZeroChilds (params IEnumerable[] values) { + // + this.validationGroup.Add (() => + this.NotZeroChilds (values) + ); + + // + return this; + } + + public XValidationProvider AddNotZeroChilds (params ICollection[] values) { + // + this.validationGroup.Add (() => + this.NotZeroChilds (values) + ); + + // + return this; + } + + public XValidationProvider AddEmailAddress ( + string emailAdress, + Exception exception = null) { + // + this.validationGroup.Add (() => + this.EmailAddress (emailAdress, exception) + ); + + // + return this; + } + + public XValidationProvider AddMobileNumber ( + string mobileNumber, + Exception exception = null) { + // + this.validationGroup.Add (() => + this.MobileNumber (mobileNumber, exception) + ); + + // + return this; + } + + public XValidationProvider AddUrl ( + string url, + Exception exception = null) { + // + this.validationGroup.Add (() => + this.Url (url, exception) + ); + + // + return this; + } + + public void ValidateGroup () { + // + // Run each Tasks ... + foreach (var action in validationGroup) { + action.Invoke (); + } + + // + // Clear Task List ... + validationGroup.Clear (); + } + + public async Task ValidateGroupAsync () { + // + // Run each Tasks ... + foreach (var action in validationGroup) { + await Task.Run (action); + } + + // + // Clear Task List ... + validationGroup.Clear (); + } + #endregion + + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Configuration\XDataBaseConfiguration.cs + +using System.Collections.Generic; +using xDataService.Constants; + +namespace xDataService.Configuration +{ + /// + /// Describe a Database ... + /// + public class XDataBaseConfiguration + { + /// + /// Provider Type ... + /// + /// + public XDbProviders Provider { get; set; } + + /// + /// Connection String which provide Requires Data to Connect to Db Provider ... + /// + /// + public string ConnectionString { get; set; } + + /// + /// Holds Seeding items ... + /// nameof(TEntity) => TEntity + /// + public IDictionary> SeedItems { get; set; } = new Dictionary>(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Configuration\XDataServiceConfiguration.cs + +using System.Collections.Generic; +using xDataService.Constants; + +namespace xDataService.Configuration +{ + /// + /// Represent Configurations of DataService Module ... + /// + public partial class XDataServiceConfiguration + { + /// + /// the base path for providing GraphQL ... + /// + /// + public string GraphQLBasePath { get; set; } = "/graphql"; + + /// + /// Data Bases Configuration ... + /// + /// + public IDictionary Databases { get; set; } = new Dictionary(); + + /// + /// Enable Soft Delete Entities or Not ... + /// + /// + public bool EnableSoftDelete { get; set; } + + /// + /// Specified Seeding Mode ... + /// + public XDbSeedingMode SeedingMode { get; set; } = XDbSeedingMode.None; + + /// + /// Enable Tracking of Entities ... + /// Only Used on EFCore ... + /// + /// + public bool EnableTracking { get; set; } = false; + + /// + /// Enable Logging Details of Errors ... + /// Only Used on EFCore ... + /// + /// + public bool EnableDetailedErrors { get; set; } = false; + + /// + /// Enable Logging Sensitive Data ... + /// Only Used on EFCore ... + /// + /// + public bool EnableSensitiveDataLogging { get; set; } = false; + + /// + /// this is a way to provide Default Pagination Data on XQuery based requests ... + /// + /// + public PagingConfiguration PagingConfiguration { get; set; } = new PagingConfiguration(); + } + + /// + /// this is a way to provide Default Pagination Data on XQuery based requests ... + /// + public partial class PagingConfiguration + { + /// + /// Default Page Size ... + /// + /// + public int DefaultPageSize { get; set; } = XDataServiceConstants.DEFAULT_PAGE_SIZE; + + /// + /// restrict Maximum Page Size ... + /// + /// + public int MaxAvailablePageSize { get; set; } = XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE; + + /// + /// restrice Minimum Page Size ... + /// + /// + public int MinAvailablePageSize { get; set; } = XDataServiceConstants.MIN_AVAILABLE_PAGE_SIZE; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Constants\ConfigurationNodeNames.cs + +namespace xDataService.Constants { + public partial struct ConfigurationNodeNames { + public const string DB_PROVIDER_NODE = "XDBProvider"; + public const string DATA_SERVICE_NODE = "DataServiceConfiguration"; + public const string DATA_SERVICE_DB_SEED_NODE = "DbSeeder"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Constants\XDataServiceConstants.cs + +namespace xDataService.Constants { + /// + /// Default Pagination Values ... + /// + public partial struct XDataServiceConstants { + public static int DEFAULT_PAGE_SIZE = 50; + public static int MAX_AVAILABLE_PAGE_SIZE = 500; + public static int MIN_AVAILABLE_PAGE_SIZE = 20; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Constants\XDbProviders.cs + +namespace xDataService.Constants { + /// + /// Represent Supported DBMS for Managing Data ... + /// + public enum XDbProviders { + None, + MySQL, + SQLite, + Oracle, + SQLServer, + MongoDB, + } + + /// + /// Represent Supported DBMS for Managing Data ... + /// + public partial struct ProviderType { + public const string MySQL = "MYSQL"; + public const string SQLite = "SQLITE"; + public const string Oracle = "ORACLE"; + public const string MongoDB = "MONGODB"; + public const string SQLServer = "SQLSERVER"; + } + +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Constants\XDbSeedingMode.cs + +namespace xDataService.Constants +{ + /// + /// All Seeding Data Mode ... + /// + public enum XDbSeedingMode + { + /// + /// No Seed Data ... + /// + None, + + /// + /// Seed Only New Items ... + /// + AddIfNotExists, + + /// + /// Seed Force (Add/Update) ... + /// + AddOrUpdate, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Constants\XRepositoryType.cs + +using xExceptions.Attributes; + +namespace xDataService.Constants +{ + public enum XRepositoryType + { + [StringValue("EF")] + EF, + + [StringValue("Mongo")] + Mongo, + + [StringValue("InMemory")] + InMemory + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Controllers\XBaseRepositoryController.cs + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xModels.Base; +using xModels.Dtos; +using xDataService.Interfaces; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using System.Threading; +using xCommons.Controllers; +using Microsoft.AspNetCore.Authorization; + +namespace xDataService.Controllers +{ + /// + /// an Abstract Controller for Providing Entity Repository Actions Implementatin ... + /// + /// + /// + [AllowAnonymous] + public abstract class XBaseRepositoryController : XBaseApiV1Controller, IXBaseRepositoryController + where TEntity : XBaseEntity + { + // + #region Properties ... + public IXBaseRepository Repository { get; } + + public Func, IOrderedQueryable> DefaultOrderBuilder { get; } + + public Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + #endregion + + // + #region Constructor ... + protected XBaseRepositoryController( + ILogger> logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXBaseRepository repository, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider + ) + { + // + Repository = repository; + DefaultOrderBuilder = defaultOrderBuilder; + DefaultIncludeBuilder = defaultIncludeBuilder; + } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// Add Specified Item ... + /// + /// + /// + /// + /// + /// + [HttpPost] + public virtual async Task> Add( + [FromBody] TEntity item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + // Get Result ... + var result = await Repository + .AddAsync( + item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add or Update Specified Item ... + /// + /// + /// + /// + /// + /// + [HttpPost("AddOrUpdate")] + public virtual async Task> AddOrUpdate( + [FromBody] TEntity item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + // Get Result ... + var result = await Repository + .AddOrUpdateAsync( + item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add a Collection of Items ... + /// + /// + /// + /// + /// + /// + [HttpPost("AddMany")] + public virtual async Task AddMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + await Repository + .AddRangeAsync( + request.Items, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Retrieve ... + /// + /// Get Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("{id}")] + public virtual async Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await Repository + .GetAsync( + id, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get All Items ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet] + public virtual async Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Get Result ... + var result = await Repository + .GetAllAsync( + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindOne/{query}")] + public virtual async Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(query); + + // + // Get Result ... + var result = await Repository + .FindOneAsync( + predicate: t => + t.PropValuesContains(query), + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find Many Items ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindMany/{query}")] + public virtual async Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(query); + + // + // Get Result ... + var result = await Repository + .FindManyAsync( + predicate: t => + t.PropValuesContains(query), + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Items Based on Query Mechanism ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + public virtual async Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(query); + + // + // Get Result ... + var result = await Repository + .QueryAsync( + query: query, + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Update ... + /// + /// Update Item ... + /// + /// + /// + /// + /// + /// + /// + [HttpPut("{id}")] + public virtual async Task> Update( + [FromRoute] TKey id, + [FromBody] TEntity item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id, item) + .ValidateGroupAsync(); + + // + // Get Result ... + var result = await Repository + .UpdateAsync( + id: id, + item: item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update a Collection of items ... + /// + /// + /// + /// + /// + /// + [HttpPost("UpdateMany")] + public virtual async Task> UpdateMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + var result = await Repository + .UpdateRangeAsync( + saveChanges: true, + items: request.Items, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Exists ... + /// + ///Check an Item Exists or not ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("{id}/IsExists")] + public virtual async Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await Repository + .IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Remove ... + /// + /// Remove an Items ... + /// + /// + /// + /// + /// + /// + /// + [HttpDelete("{id}")] + public virtual async Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await Repository + .RemoveAsync( + id: id, + saveChanges: true, + softDelete: softDelete, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Many Items ... + /// + /// + /// + /// + /// + /// + /// + [HttpPost("RemoveMany")] + public virtual async Task RemoveMany( + [FromBody] XBaseRangeRequest request, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + await Repository + .RemoveRangeAsync( + saveChanges: true, + items: request.Items, + softDelete: softDelete, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Controllers\XBaseServiceController.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Controllers; +using xCommons.Extensions; +using xCommons.Providers; +using xDataService.Interfaces; +using xExceptions.Constants; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Controllers +{ + /// + /// an Abstract Controller for Providing Entity Dto Repository Service Actions Implementatin ... + /// + /// + /// + /// + [AllowAnonymous] + public abstract class XBaseServiceController : XBaseApiV1Controller, IXBaseServiceController + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + #region Properties ... + public IXBaseRepositoryService RepositoryService { get; } + public Func, IOrderedQueryable> DefaultOrderBuilder { get; } + public Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + #endregion + + // + #region Constructor ... + protected XBaseServiceController( + ILogger> logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXBaseRepositoryService repositoryService, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider + ) + { + // + RepositoryService = repositoryService; + DefaultOrderBuilder = defaultOrderBuilder; + DefaultIncludeBuilder = defaultIncludeBuilder; + } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// Add Specified Item ... + /// + /// + /// + /// + /// + /// + [HttpPost] + public virtual async Task> Add( + [FromBody] TDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + // Get Result ... + var result = await RepositoryService + .AddAsync( + item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add or Update Specified Item ... + /// + /// + /// + /// + /// + /// + [HttpPost("AddOrUpdate")] + public virtual async Task> AddOrUpdate( + [FromBody] TDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + // Get Result ... + var result = await RepositoryService + .AddOrUpdateAsync( + item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add a Collection of Items ... + /// + /// + /// + /// + /// + /// + [HttpPost("AddMany")] + public virtual async Task AddMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + await RepositoryService + .AddRangeAsync( + request.Items, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Retrieve ... + /// + /// Get Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("{id}")] + public virtual async Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await RepositoryService + .GetAsync( + id, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get All Items ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet] + public virtual async Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Get Result ... + var result = await RepositoryService + .GetAllAsync( + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindOne/{query}")] + public virtual async Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(query); + + // + // Get Result ... + var result = await RepositoryService + .FindOneAsync( + predicate: t => + t.PropValuesContains(query), + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find Many Items ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindMany/{query}")] + public virtual async Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(query); + + // + // Get Result ... + var result = await RepositoryService + .FindManyAsync( + predicate: t => + t.PropValuesContains(query), + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Items Based on Query Mechanism ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + public virtual async Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(query); + + // + // Get Result ... + var result = await RepositoryService + .QueryAsync( + query: query, + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Update ... + /// + /// Update Item ... + /// + /// + /// + /// + /// + /// + /// + [HttpPut("{id}")] + public virtual async Task> Update( + [FromRoute] TKey id, + [FromBody] TDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id, item) + .ValidateGroupAsync(); + + // + // Get Result ... + var result = await RepositoryService + .UpdateAsync( + id: id, + item: item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update a Collection of items ... + /// + /// + /// + /// + /// + /// + [HttpPost("UpdateMany")] + public virtual async Task> UpdateMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + var result = await RepositoryService + .UpdateRangeAsync( + saveChanges: true, + items: request.Items, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Exists ... + /// + ///Check an Item Exists or not ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("{id}/IsExists")] + public virtual async Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await RepositoryService + .IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Remove ... + /// + /// Remove an Items ... + /// + /// + /// + /// + /// + /// + /// + [HttpDelete("{id}")] + public virtual async Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await RepositoryService + .RemoveAsync( + id: id, + saveChanges: true, + softDelete: softDelete, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Many Items ... + /// + /// + /// + /// + /// + /// + /// + [HttpPost("RemoveMany")] + public virtual async Task RemoveMany( + [FromBody] XBaseRangeRequest request, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + await RepositoryService + .RemoveRangeAsync( + saveChanges: true, + items: request.Items, + softDelete: softDelete, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Db\XDbContext.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Helpers; +using xDataService.Providers; + +namespace xDataService.Db +{ + /// + /// Provide an abstraction layout arround DBContext ... + /// Only Used on EFCore ... + /// + public abstract class XDbContext : DbContext + { + // + #region Properties ... + private readonly string database; + private readonly XDataServiceConfiguration config; + private readonly string[] dynamicEntityRegisterarsNames; + private readonly XEntityRegisterarHandlerService dynamicEntityHandlers; + #endregion + + // + #region Entities ... + // + // Register Default and Base Entities ... + #endregion + + // + #region Constructor ... + public XDbContext( + string database, + DbContextOptions options, + XDataServiceConfiguration config, + XEntityRegisterarHandlerService dynamicEntityHandlers, + string[] dynamicEntityRegisterarsNames = null + ) : base(options) + { + // + try + { + Database.EnsureCreated(); + Database.Migrate(); + } + catch { } + + // + this.config = config; + this.database = database; + this.dynamicEntityHandlers = dynamicEntityHandlers; + this.dynamicEntityRegisterarsNames = dynamicEntityRegisterarsNames; + + // + // Handling DbSets of Dynamic Entities ... + if (!dynamicEntityHandlers.IsNull()) + { + // + // Configure XEntityRegisterarHandlerService ... + dynamicEntityHandlers.HandleRegisteredEntities(); + + // + // Loop through Entity Types ... + foreach (var entityType in dynamicEntityHandlers.GetEntityTypes(dynamicEntityRegisterarsNames)) + { + // + try + { + // + // Use reflection to call Set() + var method = typeof(XDbContext) + .GetMethod(nameof(XDbContext.Set), Type.EmptyTypes) + .MakeGenericMethod(entityType); + if (!method.IsNull()) + { + method.Invoke(this, null); + } + } + catch { } + } + } + } + #endregion + + /// + /// Extract Current Database Configuration ... + /// + /// + public XDataBaseConfiguration GetDatabaseConfiguration() + { + // + XDataBaseConfiguration result = + config.Databases.FirstOrDefault(db => string + .Equals(db.Key, database, StringComparison.InvariantCultureIgnoreCase)).Value; + + // + return result; + } + + // + #region Overrides ... + // + // Prepare Model Configs ... + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // + OnXModelCreating(modelBuilder); + + // + // Automatically apply all IEntityTypeConfiguration in this assembly + modelBuilder.ApplyConfigurationsFromAssembly(typeof(XDbContext).Assembly); + + // + // Check Dynamic Registered Service is Exists or not ... + if (!dynamicEntityHandlers.IsNull()) + { + // + // Set Entity of Dynamically Registered Entities ... + foreach (var entityType in dynamicEntityHandlers.GetEntityTypes(dynamicEntityRegisterarsNames)) + { + modelBuilder.Entity(entityType); + } + + // + // Configure Entity using Provided Registerar ... + dynamicEntityHandlers + .HandleApplyRegisteredEntitiesConfigurations(modelBuilder, dynamicEntityRegisterarsNames); + } + + // + // Since this class inherit from Identity Db Context, + // we have to pass this to Building Identity Models ... + base.OnModelCreating(modelBuilder); + } + + // + // Prepare Configuring ... + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + // + if (config.EnableDetailedErrors) + { + optionsBuilder.EnableDetailedErrors(); + } + + // + if (config.EnableSensitiveDataLogging) + { + optionsBuilder.EnableSensitiveDataLogging(); + } + + // + if (config.EnableTracking) + { + optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll); + } + else + { + optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + } + + // + OnXConfiguring(optionsBuilder); + + // + base.OnConfiguring(optionsBuilder); + } + #endregion + + // + #region Abstracts ... + // + public abstract void OnXModelCreating(ModelBuilder modelBuilder); + + // + public abstract void OnXConfiguring(DbContextOptionsBuilder optionsBuilder); + #endregion + + // + #region Dynamic DbSet(s) ... + public override DbSet Set() + where TEntity : class + { + return base.Set(); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Db\XUnitOfWorks.cs + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using xDataService.Interfaces; +using xModels.Base; + +namespace xDataService.Db { + /// + /// Provide Unit Of Works Design Pattern for Db Transactions ... + /// Only Used on EFCore ... + /// + /// + public class XUnitOfWorks : IXUnitOfWorks + where TDbContext : XDbContext { + public TDbContext DbContext { get; } + + public XUnitOfWorks (TDbContext context) { + this.DbContext = context; + } + + public DbSet GetDbSet () + where T : XBaseEntity { + return DbContext.Set (); + } + + /// + /// Save Changes + /// + public int SaveChanges () { + return DbContext.SaveChanges (); + } + + /// + /// Save Changes Async + /// + /// + /// + public async Task SaveChangesAsync ( + CancellationToken cancellationToken = default + ) { + return await DbContext.SaveChangesAsync (cancellationToken); + } + + public void Dispose () { + DbContext.Dispose (); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\DI\XDIHelperExtension.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xCommons.Helpers; +using xDataService.Configuration; +using xDataService.Constants; +using xDataService.Extensions; +using xDataService.Helpers; +using xDataService.Models; +using xDataService.Providers; +using xExceptions.Constants; + +namespace xDataService.DI +{ + public static partial class XDIHelperExtension + { + /// + /// Retrive XDataService Configurations + /// + /// + /// + public static XDataServiceConfiguration GetXDataServiceConfiguration( + this IConfiguration source + ) + { + // + var xDataServiceConfigSection = source + .GetSection(ConfigurationNodeNames.DATA_SERVICE_NODE); + var result = xDataServiceConfigSection.Get(); + + // + // Reading Seeders ... + if (!result.IsNull() && + result.Databases.HasChild()) + { + // + result + .Databases + .ToList() + .ForEach(db => + { + // + var seedItemsSectionName = $"{ConfigurationNodeNames.DATA_SERVICE_NODE}:{nameof(XDataServiceConfiguration.Databases)}:{db.Key}:{nameof(XDataBaseConfiguration.SeedItems)}"; + var section = source.GetSection(seedItemsSectionName); + var sectionJson = source.GetSectionAsJson(seedItemsSectionName); + var sectionDict = sectionJson.FromJSON>>(); + if (!sectionDict.IsNull() && + sectionDict.Values.HasChild()) + { + // + sectionDict + .Keys + .ToList() + .ForEach(entity => + { + // + // Setting Values to Result ... + result.Databases[db.Key].SeedItems[entity] = sectionDict[entity]; + }); + } + }); + } + + // + return result; + } + + /// + /// Register XDataService Configuration ... + /// + /// + /// + public static void AddXDataServiceConfiguration( + this IServiceCollection source, + IConfiguration configuration + ) + { + // + var config = configuration.GetXDataServiceConfiguration(); + if (config.IsNullOrDefault()) + { + // + Log("Configurations Not Founded ..."); + XException.NotFound.Throw(); + } + + // + source.AddSingleton(config); + } + + /// + /// Register XDataService Configuration ... + /// + /// + /// + public static void AddXDataServiceConfiguration( + this IServiceCollection source, + XDataServiceConfiguration configuration + ) + { + // + if (configuration.IsNullOrDefault()) + { + // + Log("Configurations Not Founded ..."); + XException.NotFound.Throw(); + } + + // + source.AddSingleton(configuration); + } + + /// + /// Register a Data Base using Descriptor ... + /// + /// + /// + /// + /// + public static void AddXDatabase( + this IServiceCollection source, + IConfiguration configuration, + XDatabaseDescriptor descriptor, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + // Validate Database Descriptor ... + var isValid = + !descriptor.IsNullOrDefault() && + !descriptor.Name.IsNullOrEmpty(); + if (!isValid) + { + // + Log("Invalid Database Descriptor ..."); + return; + } + + // + // Check Service Collection Validation ... + isValid = + !source.IsNull() && + !configuration.IsNull(); + if (!isValid) + { + // + Log("Invalid Service Provider or Configuration ..."); + return; + } + + // + // Get Registere Data Service Configuration ... + var xDataServiceConfiguration = source + .GetRegisteredService(); + isValid = !xDataServiceConfiguration.IsNullOrDefault(); + if (!isValid) + { + // + // Extract Configuration from Configuration ... + xDataServiceConfiguration = configuration.GetXDataServiceConfiguration(); + isValid = !xDataServiceConfiguration.IsNullOrDefault(); + if (!isValid) + { + // + Log("DataService Configuration not provided ..."); + return; + } + + // + // Register Configuration in DI ... + source.AddSingleton(xDataServiceConfiguration); + } + + // + // Check Data Service Configuration has Database ... + isValid = + !xDataServiceConfiguration.Databases.IsNullOrDefault() && + xDataServiceConfiguration.Databases.HasChild() && + xDataServiceConfiguration.Databases.Keys.Contains(descriptor.Name); + if (!isValid) + { + // + Log("Invalid Database Descriptor ..."); + return; + } + + // + // Register Handler Service ... + source.AddSingleton(); + + // + // Configure Database Descriptor ... + descriptor.Configure(source); + + // + // Register Context ... + if (!descriptor.Context.IsNull() && + descriptor.Provider != XDbProviders.None && + !descriptor.ConnectionString.IsNullOrEmpty()) + { + // + try + { + // + typeof(XDataServiceHelper).InvokeGenericMethod( + methodName: "AddXDbContext", + runtimeType: descriptor.Context, + args: [ + source, + descriptor, + lifeTime + ] + ); + + // + Log($"DbContext: {descriptor.Name} Registered Successfully ..."); + } + catch (Exception ex) + { + Log($"DbContext: {descriptor.Name} Registration failed due {ex.Message} ..."); + } + } + + // + // Register Repositories ... + if (!descriptor.Repositories.IsNull() && + descriptor.Repositories.HasChild()) + { + // + descriptor.Repositories.ToList().ForEach(r => + { + // + source.AddXRepository( + descriptor: r, + lifeTime: lifeTime + ); + }); + } + } + + public static void AddXRepository( + this IServiceCollection source, + XRepositoryDescriptor descriptor, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + // Preparing Runtime Types ... + Type[] runtimeTypes = descriptor.GetType().GenericTypeArguments; + + // + try + { + // + typeof(XDataServiceHelper).InvokeGenericMethod( + methodName: "AddXRepository", + runtimeTypes: runtimeTypes, + args: [ + source, + descriptor, + lifeTime + ] + ); + + // + Log($"Repository of: {runtimeTypes[0]}, Registered Successfully ..."); + } + catch (Exception ex) + { + Log($"Repository of: {runtimeTypes[0]}, Registration failed due {ex.Message} ..."); + } + } + + public static void UseXDatabase( + this IApplicationBuilder source, + XDatabaseDescriptor descriptor, + bool isDevelopmentEnvironment = false + ) + { + // + // Validate ... + var isValid = + !source.IsNull() && + descriptor.IsValid(); + if (!isValid) + { + // + Log("Invalid Database Configuration ..."); + XException.InvalidArgs.Throw(); + } + + // + // Check Repositories Exists ... + isValid = descriptor.HasRepositories(); + if (isValid) + { + // + // Loop through Repositories ... + descriptor.Repositories + .ToList() + .ForEach(r => + { + // + source.UseXRepository( + descriptor: r, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + }); + } + } + + /// + /// Use a Repository Middlewares by Providing it's Descriptor ... + /// + /// + /// + /// + public static void UseXRepository( + this IApplicationBuilder source, + XRepositoryDescriptor descriptor, + bool isDevelopmentEnvironment = false + ) + { + // + Log($"Start Using Repository of: {descriptor.Entity}"); + + // + var withPlayground = false; + if (isDevelopmentEnvironment) + { + withPlayground = true; + } + + // + // Create an Scope for Services Acces ... + using (var scope = source.ApplicationServices.CreateScope()) + { + // + // Inject XDataServiceConfiguration ForMake Sure DataService Registration ... + var dataServiceConfiguration = scope.ServiceProvider.GetService(); + var isValid = !dataServiceConfiguration.IsNullOrDefault(); + if (isValid) + { + // + // Check Seeder Exists ... + // Execute Seeding Data Task if Exists ... + isValid = descriptor.HasSeeder(); + if (isValid) + { + // + var seeder = (dynamic)scope.ServiceProvider.GetService(descriptor.SeederService); + isValid = seeder != null; + if (isValid) + { + // + Log($"Start Seeding {descriptor.Entity} ..."); + + // + // Seeding ... + seeder + .Seed() + .GetAwaiter() + .GetResult(); + } + } + + // + // Check GraphQL Support ... + isValid = descriptor.HasGraphSupport(); + if (isValid) + { + // + // Use WebSockets ... + source.UseWebSockets(); + + // + // Inject GraphQL Type Helper ... + var helper = (dynamic)scope.ServiceProvider.GetService(descriptor.GraphTypeHelperService); + isValid = helper != null; + if (isValid) + { + // + var graphPath = $"{helper.GetGraphQLPath()}"; + var graphBasePath = $"{dataServiceConfiguration.GraphQLBasePath}"; + typeof(XDataServiceHelper).InvokeGenericMethod( + methodName: "UseXGraphQL", + runtimeTypes: [ + descriptor.Entity, + descriptor.Key, + descriptor.GraphType, + descriptor.GraphTypeKey, + descriptor.GraphQuery, + descriptor.GraphTypeHelperService, + descriptor.GraphTypeHelperImplementation, + descriptor.GraphSchema + ], + args: [ + source, + graphBasePath, + graphPath, + withPlayground + ] + ); + + // + Log($"Register GraphQL: {graphPath}"); + } + } + } + } + } + + // + #region Private ... + /// + /// a LogTag for xDataService ... + /// + private static string XLogTag = "xDataService"; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Extensions\DbContextOptionExtensions.cs + +using System; +using Microsoft.EntityFrameworkCore.Infrastructure; +using MySql.EntityFrameworkCore.Infrastructure; + +namespace xDataService.Extensions { + public static class DbContextOptionExtensions { + /// + /// convert dynamic object to MySqlDbContextOptionBuilder ... + /// Only Used in EFCore ... + /// + /// + /// + public static Action ToMySQLDbContextOptionsBuilder ( + this Action source + ) { + return ((Action) source); + } + + /// + /// convert dynamic object to SqliteDbContextOptionBuilder ... + /// Only Used in EFCore ... + /// + /// + /// + public static Action ToSqliteDbContextOptionsBuilder ( + this Action source + ) { + return ((Action) source); + } + + /// + /// convert dynamic object to SqlServerDbContextOptionBuilder ... + /// Only Used in EFCore ... + /// + /// + /// + public static Action ToSqlServerDbContextOptionsBuilder ( + this Action source + ) { + return ((Action) source); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Extensions\EntityExtensions.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using xCommons.Extensions; +using xModels.Base; + +namespace xDataService.Extensions { + public static class EntityExtensions { + /// + /// return all values of an entity properties as a json string ... + /// + /// + /// + /// + public static string GetPropValues (this T item) + where T : XBaseEntity { + // + var props = item.GetType ().GetProperties (); + var vals = props.Select (p => p.GetValue (item)); + + // + return vals.ToJSON (); + } + + /// + /// check values of all properties of an Entity contains specific value or not ... + /// + /// + /// + /// + /// + public static bool PropValuesContains (this T item, string value) + where T : XBaseEntity => item.GetPropValues () + .ToNormalString () + .Contains (value); + + /// + /// Retrieve Default Column Map of specific Entity ... + /// + /// + /// + /// + public static IDictionary>> GetDefaultColumnsMap (this T item) + where T : XBaseEntity { + // + if (item.IsNull ()) { + return null; + } + + // + var result = new Dictionary>> (); + var props = item.GetType ().GetProperties (); + + // + foreach (var prop in props) { + result.Add ( + prop.Name, + t => t.GetType ().GetProperty (prop.Name).GetValue (t, null)); + } + + // + return result; + } + + /// + /// Check a Type is XBaseEntity or not ... + /// + /// + /// + public static bool IsXEntity(this Type source) + { + // + var result = !source.IsNull(); + if (!result) + { + return result; + } + + // + var baseType = source.BaseType; + while(!baseType.IsNull() && baseType != typeof(object)) + { + // + if (baseType.IsGenericType && + baseType.GetGenericTypeDefinition() == typeof(XBaseEntity<>)) + { + result = true; + break; + } + + // + baseType = baseType.BaseType; + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Extensions\IQueryableExtensions.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Constants; +using xExceptions.Constants; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Extensions +{ + public static class IQueryableExtensions + { + /// + /// Apply Search Filter on a Query + /// + /// + /// + /// + /// + public static IQueryable ApplyFilter( + this IQueryable source, + string filter + ) + where T : XBaseEntity + { + // + // Apply Filter ... + return source.Where(r => r.PropValuesContains(filter)); + } + + /// + /// Apply Search Filter on a Query + /// + /// + /// + /// + /// + public static async Task> ApplyFilterAsync( + this IQueryable source, + string filter + ) + where T : XBaseEntity + { + // + // Where Clause ... + Expression> whereClause = r => r + .PropValuesContains(filter); + + // + // Generate Where Function ... + var whereFunc = whereClause.Compile(); + + // + // Get Enumerable ... + var enumerator = source + .AsAsyncEnumerable(); + + // + var result = new List(); + await + foreach (var entity in enumerator) + { + // + var isApproved = whereFunc(entity); + if (isApproved) + { + result.Add(entity); + } + } + + // + return result.AsQueryable(); + } + + /// + /// Apply Search Filter on a Enumerable + /// + /// + /// + /// + /// + public static async Task> ApplyFilterAsync( + this IEnumerable source, + string filter + ) + where T : XBaseEntity + { + // + // Where Clause ... + Expression> whereClause = r => r + .PropValuesContains(filter); + + // + // Generate Where Function ... + var whereFunc = whereClause.Compile(); + + // + // Get Enumerable ... + var enumerator = source + .ToList() + .AsQueryable(); + + // + var result = new List(); + foreach (var entity in enumerator) + { + // + var isApproved = whereFunc(entity); + if (isApproved) + { + result.Add(entity); + } + } + + // + return result + .AsEnumerable(); + } + + /// + /// Apply Search Filter on a Enumerable + /// + /// + /// + /// + /// + public static IEnumerable ApplyFilter( + this IEnumerable source, + string filter + ) + where T : class + { + // + // Apply Filter ... + return source.Where(r => r.PropValuesContains(filter)); + } + + /// + /// provide a way to Order an IQueryable by string FieldName ... + /// + /// + /// + /// + /// + public static IOrderedQueryable OrderBy( + this IQueryable query, + string propertyName + ) + { + // + var entityType = typeof(TSource); + + // + // Create x => x.PropName + var propertyInfo = entityType.GetProperty(propertyName); + if (propertyInfo.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + ParameterExpression arg = Expression.Parameter(entityType, "x"); + MemberExpression property = Expression.Property(arg, propertyName); + var selector = Expression.Lambda(property, new ParameterExpression[] { arg }); + + // + // Get System.Linq.Queryable.OrderBy() method. + var enumarableType = typeof(System.Linq.Queryable); + var method = enumarableType.GetMethods() + .Where(m => m.Name == "OrderBy" && m.IsGenericMethodDefinition) + .Where(m => + { + // + var parameters = m.GetParameters().ToList(); + + // + // Put more restriction here to ensure selecting the right overload + return parameters.Count == 2; // overload that has 2 parameters + }).Single(); + + // + // The linq's OrderBy has two generic types, which provided here ... + MethodInfo genericMethod = method + .MakeGenericMethod(entityType, propertyInfo.PropertyType); + + /* + Call query.OrderBy(selector), with query and selector: x=> x.PropName + Note that we pass the selector as Expression to the method and we don't compile it. + + By doing so EF can extract "order by" columns and generate SQL for it. + */ + var newQuery = (IOrderedQueryable)genericMethod + .Invoke(genericMethod, new object[] { query, selector }); + + // + return newQuery; + } + + /// + /// provide a way to Order an IEnumerable by string FieldName ... + /// + /// + /// + /// + /// + public static IOrderedEnumerable OrderBy( + this IEnumerable query, + string propertyName + ) + { + // + var entityType = typeof(TSource); + var propertyInfo = entityType.GetProperty(propertyName); + if (propertyInfo.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + return query.OrderBy(x => propertyInfo.GetValue(x, null)); + } + + /// + /// provide a way to Order an IQueryable by string FieldName ... + /// + /// + /// + /// + /// + public static IOrderedQueryable OrderByDescending( + this IQueryable query, + string propertyName + ) + { + // + var entityType = typeof(TSource); + + // + // Create x => x.PropName + var propertyInfo = entityType.GetProperty(propertyName); + if (propertyInfo.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + ParameterExpression arg = Expression.Parameter(entityType, "x"); + MemberExpression property = Expression.Property(arg, propertyName); + var selector = Expression.Lambda(property, new ParameterExpression[] { arg }); + + // + // Get System.Linq.Queryable.OrderBy() method. + var enumarableType = typeof(System.Linq.Queryable); + var method = enumarableType.GetMethods() + .Where(m => m.Name == "OrderByDescending" && m.IsGenericMethodDefinition) + .Where(m => + { + // + var parameters = m.GetParameters().ToList(); + + // + // Put more restriction here to ensure selecting the right overload + return parameters.Count == 2; // overload that has 2 parameters + }).Single(); + + // + // The linq's OrderBy has two generic types, which provided here ... + MethodInfo genericMethod = method + .MakeGenericMethod(entityType, propertyInfo.PropertyType); + + /* + Call query.OrderBy(selector), with query and selector: x=> x.PropName + Note that we pass the selector as Expression to the method and we don't compile it. + + By doing so EF can extract "order by" columns and generate SQL for it. + */ + var newQuery = (IOrderedQueryable)genericMethod + .Invoke(genericMethod, new object[] { query, selector }); + + // + return newQuery; + } + + /// + /// provide a way to Order an IEnumerable by string FieldName ... + /// + /// + /// + /// + /// + public static IOrderedEnumerable OrderByDescending( + this IEnumerable query, + string propertyName + ) + { + // + var entityType = typeof(TSource); + var propertyInfo = entityType.GetProperty(propertyName); + if (propertyInfo.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + return query.OrderByDescending(x => propertyInfo.GetValue(x, null)); + } + + /// + /// Apply Sorting on a query + /// + /// + /// + /// + /// + /// + /// + public static IQueryable ApplySorting( + this IQueryable source, + string sortBy, + bool isAscending, + IDictionary>> columnsMap = null + ) + where T : XBaseEntity + { + // + if (columnsMap == null) + { + // + columnsMap = source.FirstOrDefault() + .GetDefaultColumnsMap(); + + // + if (columnsMap == null) + { + return source; + } + } + + // + // Validate SortBy ... + var sortByItem = columnsMap.Keys + .FirstOrDefault(p => + p.ToNormalString() == sortBy.ToNormalString()); + if (sortByItem.IsNullOrEmpty()) + { + return source; + } + + // + // Apply Sorting ... + if (isAscending) + { + return source.OrderBy(sortByItem); + } + else + { + return source.OrderByDescending(sortByItem); + } + } + + /// + /// Apply Sorting on a query + /// + /// + /// + /// + /// + /// + /// + public static IEnumerable ApplySorting( + this IEnumerable source, + string sortBy, + bool isAscending, + IDictionary>> columnsMap = null + ) + where T : XBaseEntity + { + // + if (columnsMap == null) + { + // + columnsMap = source.FirstOrDefault() + .GetDefaultColumnsMap(); + + // + if (columnsMap == null) + { + return source; + } + } + + // + // Validate SortBy ... + var sortByItem = columnsMap.Keys + .FirstOrDefault(p => + p.ToNormalString() == sortBy.ToNormalString()); + if (sortByItem.IsNullOrEmpty()) + { + return source; + } + + // + // Apply Sorting ... + if (isAscending) + { + return source.OrderBy(sortByItem); + } + else + { + return source.OrderByDescending(sortByItem); + } + } + + /// + /// Apply Sorting on a Enumerable + /// + /// + /// + /// + /// + /// + /// + public static IEnumerable ApplySorting( + this IEnumerable source, + string sortBy, + bool isAscending, + IDictionary>> columnsMap = null + ) + where T : class + { + // + if (columnsMap == null) + { + // + columnsMap = source.FirstOrDefault() + .GetDefaultColumnsMap(); + + // + if (columnsMap == null) + { + return source; + } + } + + // + // Validate SortBy ... + var sortByItem = columnsMap.Keys + .FirstOrDefault(p => + p.ToNormalString() == sortBy.ToNormalString()); + if (sortByItem.IsNullOrEmpty()) + { + return source; + } + + // + // Apply Sorting ... + if (isAscending) + { + return source.OrderBy(sortByItem); + } + else + { + return source.OrderByDescending(sortByItem); + } + } + + /// + /// Apply Paging on a qury + /// + /// + /// + /// + /// + /// + public static IQueryable ApplyPaging( + this IQueryable source, + int? page, + int? pageSize + ) + { + // + if (!page.HasValue || + page <= 0) + { + page = 1; + } + + // + if (!pageSize.HasValue || + pageSize <= 0 || + pageSize > XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE) + { + pageSize = XDataServiceConstants.DEFAULT_PAGE_SIZE; + } + + // + var pageVal = page.GetValueOrDefault(); + var pageSizeVal = pageSize.GetValueOrDefault(); + + // + return source + .Skip((pageVal - 1) * pageSizeVal) + .Take(pageSizeVal); + } + + /// + /// Apply Paging on a Enumerable + /// + /// + /// + /// + /// + /// + public static IEnumerable ApplyPaging( + this IEnumerable source, + int? page, + int? pageSize + ) + { + // + if (!page.HasValue || + page <= 0) + { + page = 1; + } + + // + if (!pageSize.HasValue || + pageSize <= 0 || + pageSize > XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE) + { + pageSize = XDataServiceConstants.DEFAULT_PAGE_SIZE; + } + + // + var pageVal = page.GetValueOrDefault(); + var pageSizeVal = pageSize.GetValueOrDefault(); + + // + return source + .Skip((pageVal - 1) * pageSizeVal) + .Take(pageSizeVal); + } + + /// + /// Count Query Pages based on Specified Total Number ... + /// + /// + /// + /// + public static int CountPages(this XQuery source, int totalItems) + { + // + int result = 0; + + // + // Validate and Calculate ... + if (!source.IsNull() && source.PageSize > 0 && totalItems > 0) + { + // + result = totalItems / source.PageSize; + if (totalItems % source.PageSize > 0) + { + result++; + } + } + + // + return result; + } + + /// + /// Normalize an XQuery based on Data Service Configurations ... + /// + /// + /// + /// + public static XQuery NormalizeQuery( + this XQuery source, + XDataServiceConfiguration configuration // + ) + { + // + if (source.IsNull()) + { + source = new XQuery(); + } + + // + // Page Size Normalization ... + if (source.PageSize < configuration.PagingConfiguration.MinAvailablePageSize) + { + source.PageSize = configuration.PagingConfiguration.MinAvailablePageSize; + } + if (source.PageSize > configuration.PagingConfiguration.MaxAvailablePageSize) + { + source.PageSize = configuration.PagingConfiguration.MaxAvailablePageSize; + } + + // + return source; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Extensions\XDatabaseDescriptorExtensions.cs + +using xCommons.Extensions; +using xDataService.Constants; +using xDataService.Models; + +namespace xDataService.Extensions +{ + public static class XDatabaseDescriptorExtensions + { + /// + /// Validate a Database Descriptor ... + /// + /// + /// + public static bool IsValid(this XDatabaseDescriptor source) + { + // + var result = + !source.IsNull() && + !source.Name.IsNullOrEmpty(); + + // + return result; + } + + /// + /// Chack a Descriptor Has Provider or not ... + /// + /// + /// + public static bool HasProvider(this XDatabaseDescriptor source) + { + // + var result = + source.IsValid() && + source.Provider != XDbProviders.None; + + // + return result; + } + + /// + /// Check a Descriptor Has EF Core Context or not ... + /// + /// + /// + public static bool HasContext(this XDatabaseDescriptor source) + { + // + var result = + source.IsValid() && + source.HasProvider() && + source.Provider != XDbProviders.MongoDB && + !source.Context.IsNull(); + + // + return result; + } + + /// + /// Check a Descriptor Has Repositories or not ... + /// + /// + /// + public static bool HasRepositories(this XDatabaseDescriptor source) + { + // + var result = + source.IsValid() && + source.HasProvider() && + !source.Repositories.IsNull() && + source.Repositories.HasChild(); + + // + return result; + } + + /// + /// Check a Descriptor Has MongoDB Convention Packs ... + /// + /// + /// + public static bool HasConventionPacks(this XDatabaseDescriptor source) + { + // + var result = + source.IsValid() && + source.HasProvider() && + source.Provider == XDbProviders.MongoDB && + !source.ConventionPacks.IsNull(); + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Extensions\XDataServiceConfigurationExtensions.cs + +using System.Linq; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Constants; + +namespace xDataService.Extensions { + public static class XDataServiceConfigurationExtensions { + /// + /// Extract Provider enum from Provider Type ... + /// + /// + /// + public static XDbProviders ToDbProvider (this string source) { + // + if (source.IsNullOrEmpty ()) { + return XDbProviders.None; + } + + // + source = source.ToNormalString (); + + // + var result = source == ProviderType.MySQL.ToNormalString () ? + XDbProviders.MySQL : + source == ProviderType.SQLite.ToNormalString () ? + XDbProviders.SQLite : + source == ProviderType.SQLServer.ToNormalString () ? + XDbProviders.SQLServer : + source == ProviderType.MongoDB.ToNormalString () ? + XDbProviders.MongoDB : + XDbProviders.None; + + // + return result; + } + + /// + /// Retrieve Mongo URI from ConnectionString + /// + /// + /// + public static string GetMongoDbURI (this XDataBaseConfiguration source) { + // + var parts = source.GetConnectionParts (); + var result = parts + .Where (p => p.Trim ().StartsWith ("Uri=")) + .FirstOrDefault (); + result = result.Replace ("Uri=", "").Trim (); + + // + return result; + } + + /// + /// Retrieve Mongo Database Name from Connaction string + /// + /// + /// + public static string GetMongoDbDatabase (this XDataBaseConfiguration source) { + // + var parts = source.GetConnectionParts (); + var result = parts + .Where (p => p.Trim ().StartsWith ("Database=")) + .FirstOrDefault (); + result = result.Replace ("Database=", "").Trim (); + + // + return result; + } + + // + #region Private ... + private static string[] GetConnectionParts (this XDataBaseConfiguration source) { + // + var result = source.ConnectionString.Split (';'); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Extensions\XGraphQLExtensions.cs + +using GraphQL; +using GraphQL.Types; +using xDataService.Constants; +using xModels.Dtos; + +namespace xDataService.Extensions { + public static class XGraphQLExtensions { + public static TKey GetIdArgument (this IResolveFieldContext source) { + return source.GetArgument (XGraphQLHelper.GetIdArgument().Name); + } + + public static bool GetIgnoreSoftDeletedArgument (this IResolveFieldContext source) { + return source.GetArgument (XGraphQLHelper.IgnoreSoftDeletedArgument.Name); + } + + public static string GetSearchQueryArgument (this IResolveFieldContext source) { + return source.GetArgument (XGraphQLHelper.SearchQueryArgument.Name); + } + + public static XQuery GetQueryArgument (this IResolveFieldContext source) { + return source.GetArgument (XGraphQLHelper.QueryArgument.Name); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Extensions\XMongoDbExtensions.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Bson; +using MongoDB.Driver; +using MongoDB.Driver.Core.Bindings; +using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.Servers; +using MongoDB.Driver.Core.WireProtocol.Messages.Encoders; +using MongoDB.Driver.Linq; +using xDataService.Mongo; + +namespace xDataService.Extensions { + public static class XMongoDbExtensions { + public static Guid ToGuid (this ObjectId source) { + // + var bytes = source + .ToByteArray () + .Concat (new byte[] { 5, 5, 5, 5 }) + .ToArray (); + + // + var result = new Guid (bytes); + + // + return result; + } + + public static ObjectId ToObjectId (this Guid source) { + // + var bytes = source + .ToByteArray () + .Take (12) + .ToArray (); + + // + var result = new ObjectId (bytes); + + // + return result; + } + + public static IAsyncEnumerable ToAsyncEnumerable (this IAsyncCursorSource asyncCursorSource) { + return new XAsyncEnumerableAdapter (asyncCursorSource); + } + + public static async IAsyncEnumerable ToAsyncEnumerable (this IAsyncCursor source) { + while (await source.MoveNextAsync ()) { + foreach (var current in source.Current) { + yield return current; + } + } + } + + public static XMongoAsyncCursor ToAsyncCursor (this IQueryable source) { + return new XMongoAsyncCursor (source); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Extensions\XReferenceExtensions.cs + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using xCommons.Extensions; +using xDataService.Models; + +namespace xDataService.Extensions +{ + public static class XReferenceExtensions + { + /// + /// Converts an XReference to String Representation ... + /// + /// + /// + /// + /// + public static string ToXReferenceString( + this XReference source, + char? splitter = '_' + ) + { + // + var result = string.Empty; + + // + // Normalize Splitter ... + if (!splitter.HasValue) + { + splitter = '_'; + } + + // + if (!source.IsNullOrDefault() && source.Index >= 0 && !source.ReferencedTo.IsNull()) + { + result = $"{source.ProvidedFor}{splitter.Value}{source.ReferencedTo}{splitter.Value}{source.Index}"; + } + + // + return result; + } + + /// + /// Parse an String and Extract to XReference if it's Described ... + /// + /// + /// + /// + /// + public static XReference FromXReference( + this string source, + char? splitter = '_' + ) + { + // + XReference result = null; + + // + // Normalize Splitter ... + if (!splitter.HasValue) + { + splitter = '_'; + } + + // + if (!source.IsNullOrEmpty()) + { + // + var parts = source.Split(splitter.Value); + if (parts.Count() >= 3) + { + // + T referencedTo; + try + { + referencedTo = (T)Convert.ChangeType(parts[1], typeof(T)); + } catch + { + referencedTo = (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(parts[1]); + } + + // + // Parse String Parts ... + result = new XReference + { + ReferencedTo = referencedTo, + Index = (int)Convert.ChangeType(parts[2], typeof(int)), + ProvidedFor = (string)Convert.ChangeType(parts[0], typeof(string)), + }; + } + } + + // + return result; + } + + /// + /// Converts a Collection of XReference to it's String Representation ... + /// + /// + /// + /// + public static string ToXReferenceString(this IEnumerable> source) + { + // + var result = string.Empty; + + // + if (!source.IsNull() && source.HasChild()) + { + // + var stringList = new List(); + foreach (var s in source) + { + // + var sr = ToXReferenceString(s); + if (!sr.IsNullOrEmpty()) + { + stringList.Add(sr); + } + } + + // + result = stringList.ToListString(); + } + + // + return result; + } + public static string ToXReferenceString(this ICollection> source) + { + // + var result = source + .AsEnumerable() + .ToXReferenceString(); + + // + return result; + } + public static string ToXReferenceString(this IList> source) + { + // + var result = source + .AsEnumerable() + .ToXReferenceString(); + + // + return result; + } + + /// + /// Parse an string to XReference Collection Representation ... + /// + /// + /// + /// + public static IList> ParseXReferenceList(this string source) + { + // + var result = new List>(); + + // + if (!source.IsNullOrEmpty()) + { + // + var stringList = source.ParseListString(); + if (!stringList.IsNull() && stringList.HasChild()) + { + // + foreach (var s in stringList) + { + // + var sr = FromXReference(s); + if (!sr.IsNullOrDefault()) + { + result.Add(sr); + } + } + } + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Extensions\XRepositoryDescriptorExtensions.cs + +using xCommons.Extensions; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Models; +using xDataService.Providers; +using xModels.Base; + +namespace xDataService.Extensions +{ + public static class XRepositoryDescriptorExtensions + { + // + #region Hasers ... + /// + /// Validate a Descriptor ... + /// + /// + /// + public static bool IsValid(this XRepositoryDescriptor source) + { + var result = + !source.IsNull() && + !source.Key.IsNull() && + !source.Entity.IsNull(); + + // + return result; + } + + /// + /// Check a Descriptor Has Events Service or not ... + /// + /// + /// + public static bool HasEvents(this XRepositoryDescriptor source) + { + // + var result = + source.IsValid() && + !source.EventsService.IsNull() && + !source.EventsImplementation.IsNull(); + + // + return result; + } + + /// + /// Check a Descriptor Has EF Core Context or not ... + /// + /// + /// + public static bool HasContext(this XRepositoryDescriptor source) + { + // + var result = + source.IsValid() && + !source.Context.IsNull(); + + // + return result; + } + + /// + /// Check a Descriptor Has GraphQL Support or not ... + /// + /// + /// + public static bool HasGraphSupport(this XRepositoryDescriptor source) + { + // + var result = + source.IsValid() && + source.HasRepository() && + !source.GraphType.IsNull() && + !source.GraphQuery.IsNull() && + !source.GraphSchema.IsNull() && + !source.GraphTypeKey.IsNull() && + !source.GraphTypeHelperService.IsNull() && + !source.GraphTypeHelperImplementation.IsNull(); + + // + return result; + } + + /// + /// Check a Descriptor Has Key Generator Service or not ... + /// + /// + /// + public static bool HasKeyGenerator(this XRepositoryDescriptor source) + { + // + var result = + source.IsValid() && + !source.KeyGeneratorService.IsNull() && + !source.KeyGeneratorImplementation.IsNull(); + + // + return result; + } + + /// + /// Check a Descriptor Has Repository Service or not ... + /// + /// + /// + public static bool HasRepository(this XRepositoryDescriptor source) + { + // + var result = + source.IsValid() && + !source.RepositoryService.IsNull() && + !source.RepositoryImplementation.IsNull(); + + // + return result; + } + + /// + /// Check a Descriptor Has Seeder or not ... + /// + /// + /// + public static bool HasSeeder(this XRepositoryDescriptor source) + { + // + var result = + source.IsValid() && + source.HasRepository() && + !source.SeederService.IsNull() && + !source.SeederImplementation.IsNull(); + + // + return result; + } + #endregion + + // + #region Builders ... + public static XRepositoryDescriptor AddEntity( + this XRepositoryDescriptor source + ) + where TEntity : XBaseEntity + { + // + if (source.IsNull()) + { + source = new XRepositoryDescriptor(); + } + + // + source.Key = typeof(TKey); + source.Entity = typeof(TEntity); + + // + return source; + } + + public static XRepositoryDescriptor AddEvents( + this XRepositoryDescriptor source + ) + where TEntity : XBaseEntity + where TEventsService : IXBaseRepositoryEvents + where TEventsImplementation : XBaseRepositoryEvents + { + // + if (source.IsNull()) + { + // + source = new XRepositoryDescriptor(); + source = source.AddEntity(); + } + + // + source.EventsService = typeof(TEventsService); + source.EventsImplementation = typeof(TEventsImplementation); + + // + return source; + } + + public static XRepositoryDescriptor AddKeyGenerator( + this XRepositoryDescriptor source + ) + where TEntity : XBaseEntity + where TKeyGeneratorService : IXKeyGenerator + where TKeyGeneratorImplementation : XKeyGenerator + { + // + if (source.IsNull()) + { + // + source = new XRepositoryDescriptor(); + source = source.AddEntity(); + } + + // + source.KeyGeneratorService = typeof(TKeyGeneratorService); + source.KeyGeneratorImplementation = typeof(TKeyGeneratorImplementation); + + // + return source; + } + + public static XRepositoryDescriptor AddSeeder( + this XRepositoryDescriptor source + ) + where TEntity : XBaseEntity + where TSeederService : IXBaseDbSeeder + where TSeederImplementation : XBaseDbSeeder + { + // + if (source.IsNull()) + { + // + source = new XRepositoryDescriptor(); + source = source.AddEntity(); + } + + // + source.SeederService = typeof(TSeederService); + source.SeederImplementation = typeof(TSeederImplementation); + + // + return source; + } + + public static XRepositoryDescriptor AddEFRepository( + this XRepositoryDescriptor source + ) + where TContext : XDbContext + where TEntity : XBaseEntity + where TRepositoryService : IXBaseRepository + where TRepositoryImplementation : XBaseEFRepository + { + // + if (source.IsNull()) + { + // + source = new XRepositoryDescriptor(); + source = source.AddEntity(); + } + + // + source.RepositoryService = typeof(TRepositoryService); + source.RepositoryImplementation = typeof(TRepositoryImplementation); + + // + return source; + } + + public static XRepositoryDescriptor AddMongoRepository( + this XRepositoryDescriptor source + ) + where TEntity : XBaseEntity + where TRepositoryService : IXBaseRepository + where TRepositoryImplementation : XBaseMongoRepository + { + // + if (source.IsNull()) + { + // + source = new XRepositoryDescriptor(); + source = source.AddEntity(); + } + + // + source.RepositoryService = typeof(TRepositoryService); + source.RepositoryImplementation = typeof(TRepositoryImplementation); + + // + return source; + } + + public static XRepositoryDescriptor AddInMemoryRepository( + this XRepositoryDescriptor source + ) + where TEntity : XBaseEntity + where TRepositoryService : IXBaseRepository + where TRepositoryImplementation : XBaseInMemoryRepository + { + // + if (source.IsNull()) + { + // + source = new XRepositoryDescriptor(); + source = source.AddEntity(); + } + + // + source.RepositoryService = typeof(TRepositoryService); + source.RepositoryImplementation = typeof(TRepositoryImplementation); + + // + return source; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\GraphQL\XBaseGraphObjectType.cs + +using GraphQL.Types; +using xModels.Base; + +namespace xDataService.GraphQL +{ + /// + /// Base Entity GraphType Object ... + /// + /// + public abstract class XBaseGraphObjectType : ObjectGraphType + where TEntity : XBaseEntity { + public XBaseGraphObjectType () { + // + Name = $"{typeof(TEntity).Name}GraphType"; + + // + Field (x => x.Id); + Field (x => x.Deleted); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\GraphQL\XBaseGraphQLEventType.cs + +using GraphQL.Types; +using xDataService.Models; +using xModels.Base; + +namespace xDataService.GraphQL { + public class XBaseGraphQLEventType : ObjectGraphType> + where TGraphType : IGraphType + where TEntity : XBaseEntity { + public XBaseGraphQLEventType () { + // + Name = nameof (XBaseGraphQLEventType); + + // + Field (x => x.Model, type : typeof (TGraphType)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\GraphQL\XBaseGraphQLQuery.cs + +using System; +using System.Linq.Expressions; +using GraphQL.Types; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Constants; +using xDataService.Extensions; +using xDataService.Interfaces; +using xDataService.Models; +using xModels.Base; + +namespace xDataService.GraphQL +{ + public abstract class XBaseGraphQLQuery : ObjectGraphType, IXBaseGraphQLQuery + where TEntity : XBaseEntity + where TGraph : IGraphType + where TGraphKey : IGraphType + { + // + protected readonly XDataServiceConfiguration configuration; + protected readonly IXBaseRepository repository; + protected readonly IXBaseGraphQLTypeHelper helper; + + // + public XBaseGraphQLQuery( + XDataServiceConfiguration configuration, + IXBaseRepository repository, + IXBaseGraphQLTypeHelper helper + ) + { + // + this.helper = helper; + this.repository = repository; + this.configuration = configuration; + + // + Name = GetType().Name; + + // + #region Retrieve ... + // + // Get ... + FieldAsync( + name: helper.GetInQuerySingleName(), + arguments: new QueryArguments( + XGraphQLHelper.GetIdArgument(), + XGraphQLHelper.IgnoreSoftDeletedArgument + ), + resolve: async context => + await repository + .GetAsync( + includeBuilder: null, + id: context.GetIdArgument(), + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument() + ) + .ToDynamicObject() + ); + + // + // GetAll ... + FieldAsync>( + name: helper.GetInQueryCollectionName(), + arguments: new QueryArguments( + XGraphQLHelper.IgnoreSoftDeletedArgument + ), + resolve: async context => + await repository + .GetAllAsync( + orderBuilder: null, + includeBuilder: null, + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument() + ) + .ToDynamicObject() + ); + + // + // FindOne ... + FieldAsync( + name: helper.GetFindOneName(), + arguments: new QueryArguments( + XGraphQLHelper.SearchQueryArgument, + XGraphQLHelper.IgnoreSoftDeletedArgument + ), + resolve: async (context) => + { + // + var searchQuery = context.GetSearchQueryArgument(); + Expression> whereClase = pe => pe + .PropValuesContains(searchQuery); + + // + return await repository + .FindOneAsync( + includeBuilder: null, + predicate: whereClase, + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument() + ) + .ToDynamicObject(); + } + ); + + // + // FindMany ... + FieldAsync>( + name: helper.GetFindManyName(), + arguments: new QueryArguments( + XGraphQLHelper.SearchQueryArgument, + XGraphQLHelper.IgnoreSoftDeletedArgument + ), + resolve: async (context) => + { + // + var searchQuery = context.GetSearchQueryArgument(); + Expression> whereClase = pe => pe + .PropValuesContains(searchQuery); + + // + return await repository + .FindManyAsync( + orderBuilder: null, + includeBuilder: null, + predicate: whereClase, + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument() + ) + .ToDynamicObject(); + } + ); + + // + // Query ... + FieldAsync>( + name: helper.GetQueryName(), + arguments: new QueryArguments( + XGraphQLHelper.QueryArgument, + XGraphQLHelper.IgnoreSoftDeletedArgument + ), + resolve: async context => + await repository + .QueryAsync( + predicate: null, + orderBuilder: null, + includeBuilder: null, + query: context.GetQueryArgument(), + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument() + ) + .ToDynamicObject() + ); + + // + // Count ... + FieldAsync( + name: helper.GetCountName(), + arguments: new QueryArguments( + XGraphQLHelper.IgnoreSoftDeletedArgument + ), + resolve: async context => + await repository + .CountAsync( + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument() + ) + ); + + // + // Exists ... + FieldAsync( + name: helper.GetExistsName(), + arguments: new QueryArguments( + XGraphQLHelper.GetIdArgument(), + XGraphQLHelper.IgnoreSoftDeletedArgument + ), + resolve: async context => + await repository.IsExistsAsync( + id: context.GetIdArgument(), + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument() + ) + ); + #endregion + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\GraphQL\XBaseGraphQLSubscription.cs + +using System.Collections.Generic; +using GraphQL.Resolvers; +using GraphQL.Types; +using xCommons.Extensions; +using xDataService.Providers; +using xDataService.Interfaces; +using xDataService.Models; +using xModels.Base; + +namespace xDataService.GraphQL { + public abstract class XBaseGraphQLSubscription : ObjectGraphType + where TGraph : IGraphType + where TEntity : XBaseEntity { + public XBaseGraphQLSubscription (IXBaseRepositoryEvents eventProvider = null) { + // + Name = GetType ().Name; + + // + // Check if base repository patter registered ... + if (!eventProvider.IsNull ()) { + // + // Add ... + AddField (new EventStreamFieldType { + // + Name = nameof (XBaseRepositoryEvents.AddEvent), + Type = typeof (XBaseGraphQLEventType), + Resolver = new FuncFieldResolver> ( + context => context.Source as XBaseEventModel + ), + Subscriber = new EventStreamResolver> (context => { + return eventProvider.OnAddObservable; + }) + }); + + // + // Add Many ... + AddField (new EventStreamFieldType { + // + Name = nameof (XBaseRepositoryEvents.AddManyEvent), + Type = typeof (XBaseGraphQLEventType), + Resolver = new FuncFieldResolver>> ( + context => context.Source as XBaseEventModel> + ), + Subscriber = new EventStreamResolver>> (context => { + return eventProvider.OnAddManyObservable; + }) + }); + + // + // Update ... + AddField (new EventStreamFieldType { + // + Name = nameof (XBaseRepositoryEvents.UpdateEvent), + Type = typeof (XBaseGraphQLEventType), + Resolver = new FuncFieldResolver> ( + context => context.Source as XBaseEventModel + ), + Subscriber = new EventStreamResolver> (context => { + return eventProvider.OnUpdateObservable; + }) + }); + + // + // Update Mnay ... + AddField (new EventStreamFieldType { + // + Name = nameof (XBaseRepositoryEvents.UpdateManyEvent), + Type = typeof (XBaseGraphQLEventType), + Resolver = new FuncFieldResolver>> ( + context => context.Source as XBaseEventModel> + ), + Subscriber = new EventStreamResolver>> (context => { + return eventProvider.OnUpdateManyObservable; + }) + }); + + // + // Remove ... + AddField (new EventStreamFieldType { + // + Name = nameof (XBaseRepositoryEvents.RemoveEvent), + Type = typeof (XBaseGraphQLEventType), + Resolver = new FuncFieldResolver> ( + context => context.Source as XBaseEventModel + ), + Subscriber = new EventStreamResolver> (context => { + return eventProvider.OnRemoveObservable; + }) + }); + + // + // Remove Many ... + AddField (new EventStreamFieldType { + // + Name = nameof (XBaseRepositoryEvents.RemoveManyEvent), + Type = typeof (XBaseGraphQLEventType), + Resolver = new FuncFieldResolver>> ( + context => context.Source as XBaseEventModel> + ), + Subscriber = new EventStreamResolver>> (context => { + return eventProvider.OnRemoveManyObservable; + }) + }); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\GraphQL\XBaseGraphQLTypeHelper.cs + +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Interfaces; +using xModels.Base; + +namespace xDataService.GraphQL +{ + public abstract class XBaseGraphQLTypeHelper : IXBaseGraphQLTypeHelper + where TEntity : XBaseEntity + { + // + private readonly XDataServiceConfiguration configuration; + + // + protected XBaseGraphQLTypeHelper( + XDataServiceConfiguration configuration = null + ) + { + this.configuration = configuration; + } + + // + public abstract string GetInQueryCollectionName(); + + public abstract string GetInQuerySingleName(); + + // + public string GetFindOneName() + { + return $"find{GetInQuerySingleName().ToNormalString().Capitalize()}"; + } + + public string GetFindManyName() + { + return $"find{GetInQueryCollectionName().ToNormalString().Capitalize()}"; + } + + public string GetQueryName() + { + return $"query{GetInQueryCollectionName().ToNormalString().Capitalize()}"; + } + + public string GetCountName() + { + return $"count{GetInQueryCollectionName().ToNormalString().Capitalize()}"; + } + + public string GetExistsName() + { + return $"exists{GetInQuerySingleName().ToNormalString().Capitalize()}"; + } + + public string GetGraphQLPath(string baseGraphPath = null) + { + // + baseGraphPath = baseGraphPath.IsNullOrEmpty() ? + configuration.IsNull() || configuration.GraphQLBasePath.IsNullOrEmpty() ? + "" : + configuration.GraphQLBasePath : + baseGraphPath; + + // + var basePath = baseGraphPath + .EndsWith("/") ? + baseGraphPath + .Substring(0, baseGraphPath.Length - 1) : + baseGraphPath; + + // + return $"{basePath}/{GetInQueryCollectionName().ToNormalString()}"; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Helpers\XCursorHelper.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using xCommons.Extensions; + +namespace xDataService.Helpers { + public partial class XCursorHelper { + public static string ToCursor (T id) { + // + // Check Numbers ... + if (id.ToString ().IsDigits ()) { + return Convert + .ToBase64String (BitConverter + .GetBytes (Convert.ToInt32 (id)) + ); + } else + // + // Check Guid ... + if (id.ToString ().IsGuid ()) { + return Convert + .ToBase64String (new Guid (id.ToString ()) + .ToByteArray ()); + } else + // + // Assume as String ... + { + return Convert + .ToBase64String (id + .ToString ().ToBytes ()); + } + } + + public static T FromCursor (string cursor) { + // + // Check Numbers ... + var returnType = typeof (T); + if (returnType == typeof (int) || + returnType == typeof (long) || + returnType == typeof (decimal)) { + return Convert.ChangeType ((BitConverter + .ToInt32 (Convert + .FromBase64String (cursor), + 0 + )) + .ToDynamicObject (), returnType); + } else + // + // Check Guid ... + if (returnType == typeof (Guid)) { + return Convert.ChangeType ((new Guid ( + Convert.FromBase64String (cursor) + )) + .ToDynamicObject (), returnType); + } else + // + // Assume as String ... + { + return Convert.ChangeType (( + Convert.FromBase64String (cursor) + ) + .ToDynamicObject (), returnType); + } + } + + public static (string firstCursor, string lastCursor) GetFirstAndLastCursor (IEnumerable ids) { + // + if (ids?.Any () != true) { + return (null, null); + } + + // + var firstCursor = ToCursor (ids.First ()); + var lastCursor = ToCursor (ids.Last ()); + + // + return (firstCursor, lastCursor); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Helpers\XDataServiceHelper.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using GraphQL; +using GraphQL.Server; +using GraphQL.Server.Ui.Playground; +using GraphQL.Server.Ui.Voyager; +using GraphQL.Types; +using Microsoft.AspNetCore.Builder; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Conventions; +using MySql.EntityFrameworkCore.Extensions; +using xCommons.Extensions; +using xDataService.Constants; +using xDataService.Db; +using xDataService.Extensions; +using xDataService.GraphQL; +using xDataService.Interfaces; +using xDataService.Models; +using xDataService.Providers; +using xExceptions.Constants; +using xModels.Base; + +namespace xDataService.Helpers +{ + public static class XDataServiceHelper + { + /// + /// Register DbContext ... + /// + /// + /// + /// + /// + public static void AddXDbContext( + IServiceCollection source, + XDatabaseDescriptor databaseDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TContext : XDbContext + { + // + var isValid = + databaseDescriptor.IsValid() && + databaseDescriptor.HasProvider(); + if (!isValid) + { + XException.InvalidConfiguration.Throw(); + } + + // + switch (databaseDescriptor.Provider) + { + // + // MySQL ... + case XDbProviders.MySQL: + // + source.AddEntityFrameworkMySQL(); + source.AddDbContext(cfg => + { + // + cfg.UseMySQL( + databaseDescriptor.ConnectionString, + databaseDescriptor.OptionsBuilder + ); + }, lifetime); + break; + + // + // SQLite ... + case XDbProviders.SQLite: + // + source.AddEntityFrameworkSqlite(); + source.AddDbContext(cfg => + { + // + cfg.UseSqlite( + databaseDescriptor.ConnectionString, + databaseDescriptor.OptionsBuilder + ); + }, lifetime); + break; + + // + // Oracle ... + case XDbProviders.Oracle: + // + source.AddEntityFrameworkOracle(); + source.AddDbContext(cfg => + { + // + cfg.UseOracle( + databaseDescriptor.ConnectionString, + databaseDescriptor.OptionsBuilder + ); + }, lifetime); + break; + + // + // SQLServer ... + case XDbProviders.SQLServer: + // + source.AddEntityFrameworkSqlServer(); + source.AddDbContext(cfg => + { + // + cfg.UseSqlServer( + databaseDescriptor.ConnectionString, + databaseDescriptor.OptionsBuilder + ); + }, lifetime); + break; + + // + // Mongo DB ... + case XDbProviders.MongoDB: + // + #region XBaseEntity Mappers ... + // + // Map ID as BsonId here ... + BsonClassMap.RegisterClassMap>(cm => + { + // + cm.AutoMap(); + cm.MapIdMember(c => c.Id); + }); + BsonClassMap.RegisterClassMap>(cm => + { + // + cm.AutoMap(); + cm.MapIdMember(c => c.Id); + }); + BsonClassMap.RegisterClassMap>(cm => + { + // + cm.AutoMap(); + cm.MapIdMember(c => c.Id); + }); + BsonClassMap.RegisterClassMap>(cm => + { + // + cm.AutoMap(); + cm.MapIdMember(c => c.Id); + }); + #endregion + + // + // Check Has Convention Packs ... + isValid = databaseDescriptor.HasConventionPacks(); + if (isValid) + { + // + ConventionRegistry.Register( + nameof( + databaseDescriptor.ConventionPacks + ), + databaseDescriptor.ConventionPacks, + type => !type.FullName + .IsNullOrEmpty() + ); + } + break; + } + + // + isValid = + databaseDescriptor.Provider == XDbProviders.MySQL || + databaseDescriptor.Provider == XDbProviders.SQLite || + databaseDescriptor.Provider == XDbProviders.Oracle || + databaseDescriptor.Provider == XDbProviders.SQLServer; + if (isValid) + { + // + AddXUnitOfWorks( + source, + lifetime + ); + } + } + + // + #region Repository ... + /// + /// Register Repository ... + /// + /// + /// + /// + /// + /// + /// + public static void AddXRepository( + IServiceCollection source, + XRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TEntity : XBaseEntity + { + // + var isValid = repositoryDescriptor.IsValid(); + if (!isValid) + { + return; + } + } + + /// + /// Register Repository ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXRepository< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation + >( + IServiceCollection source, + XRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TEntity : XBaseEntity + where TKeyGeneratorService : IXKeyGenerator + where TKeyGeneratorImplementation : XKeyGenerator + { + // + var isValid = repositoryDescriptor.IsValid(); + if (!isValid) + { + return; + } + + // + // Register KeyGenerator ... + isValid = repositoryDescriptor.HasKeyGenerator(); + if (isValid) + { + // + AddXKeyGenerator( + source, + repositoryDescriptor, + lifetime + ); + } + } + + /// + /// Register Repository ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXRepository< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation + >( + IServiceCollection source, + XRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TEntity : XBaseEntity + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + { + // + var isValid = repositoryDescriptor.IsValid(); + if (!isValid) + { + return; + } + + // + // Register KeyGenerator ... + isValid = repositoryDescriptor.HasKeyGenerator(); + if (isValid) + { + // + AddXKeyGenerator( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Events ... + isValid = repositoryDescriptor.HasEvents(); + if (isValid) + { + // + AddXEvents( + source, + repositoryDescriptor, + lifetime + ); + } + } + + /// + /// Register Repository ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXRepository< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema + >( + IServiceCollection source, + XRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + // + var isValid = repositoryDescriptor.IsValid(); + if (!isValid) + { + return; + } + + // + // Register KeyGenerator ... + isValid = repositoryDescriptor.HasKeyGenerator(); + if (isValid) + { + // + AddXKeyGenerator( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Events ... + isValid = repositoryDescriptor.HasEvents(); + if (isValid) + { + // + AddXEvents( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Graph ... + isValid = repositoryDescriptor.HasGraphSupport(); + if (isValid) + { + // + AddXGraphQL( + source, + repositoryDescriptor, + lifetime + ); + } + } + + /// + /// Register Repository ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXRepository< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation + >( + IServiceCollection source, + XInMemoryRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TRepositoryImplementation : XBaseInMemoryRepository + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + // + var isValid = repositoryDescriptor.IsValid(); + if (!isValid) + { + return; + } + + // + // Register KeyGenerator ... + isValid = repositoryDescriptor.HasKeyGenerator(); + if (isValid) + { + // + AddXKeyGenerator( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Events ... + isValid = repositoryDescriptor.HasEvents(); + if (isValid) + { + // + AddXEvents( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Graph ... + isValid = repositoryDescriptor.HasGraphSupport(); + if (isValid) + { + // + AddXGraphQL( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Repository ... + isValid = repositoryDescriptor.HasRepository(); + if (isValid) + { + // + source.Add( + new ServiceDescriptor( + repositoryDescriptor.RepositoryService, + repositoryDescriptor.RepositoryImplementation, + lifetime + ) + ); + } + } + + /// + /// Register Repository ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXRepository< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TSeederService, + TSeederImplementation + >( + IServiceCollection source, + XInMemoryRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TSeederService : IXBaseDbSeeder + where TSeederImplementation : XBaseDbSeeder + where TGraph : XBaseGraphObjectType + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TRepositoryImplementation : XBaseInMemoryRepository + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + // + var isValid = repositoryDescriptor.IsValid(); + if (!isValid) + { + return; + } + + // + // Register KeyGenerator ... + isValid = repositoryDescriptor.HasKeyGenerator(); + if (isValid) + { + // + AddXKeyGenerator( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Events ... + isValid = repositoryDescriptor.HasEvents(); + if (isValid) + { + // + AddXEvents( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Graph ... + isValid = repositoryDescriptor.HasGraphSupport(); + if (isValid) + { + // + AddXGraphQL( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Repository ... + isValid = repositoryDescriptor.HasRepository(); + if (isValid) + { + // + source.Add( + new ServiceDescriptor( + repositoryDescriptor.RepositoryService, + repositoryDescriptor.RepositoryImplementation, + lifetime + ) + ); + } + + // + // DbSeeder ... + isValid = repositoryDescriptor.HasSeeder(); + if (isValid) + { + // + source.Add( + new ServiceDescriptor( + repositoryDescriptor.SeederService, + repositoryDescriptor.SeederImplementation, + lifetime + ) + ); + } + } + + /// + /// Register Repository ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXRepository< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation + >( + IServiceCollection source, + XMongoRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TRepositoryImplementation : XBaseMongoRepository + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + // + var isValid = repositoryDescriptor.IsValid(); + if (!isValid) + { + return; + } + + // + // Register KeyGenerator ... + isValid = repositoryDescriptor.HasKeyGenerator(); + if (isValid) + { + // + AddXKeyGenerator( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Events ... + isValid = repositoryDescriptor.HasEvents(); + if (isValid) + { + // + AddXEvents( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Graph ... + isValid = repositoryDescriptor.HasGraphSupport(); + if (isValid) + { + // + AddXGraphQL( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Repository ... + isValid = repositoryDescriptor.HasRepository(); + if (isValid) + { + // + source.Add( + new ServiceDescriptor( + repositoryDescriptor.RepositoryService, + repositoryDescriptor.RepositoryImplementation, + lifetime + ) + ); + } + } + + /// + /// Register Repository ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXRepository< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TSeederService, + TSeederImplementation + >( + IServiceCollection source, + XMongoRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TSeederService : IXBaseDbSeeder + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TSeederImplementation : XBaseDbSeeder + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TRepositoryImplementation : XBaseMongoRepository + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + // + var isValid = repositoryDescriptor.IsValid(); + if (!isValid) + { + return; + } + + // + // Register KeyGenerator ... + isValid = repositoryDescriptor.HasKeyGenerator(); + if (isValid) + { + // + AddXKeyGenerator( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Events ... + isValid = repositoryDescriptor.HasEvents(); + if (isValid) + { + // + AddXEvents( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Graph ... + isValid = repositoryDescriptor.HasGraphSupport(); + if (isValid) + { + // + AddXGraphQL( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Repository ... + isValid = repositoryDescriptor.HasRepository(); + if (isValid) + { + // + source.Add( + new ServiceDescriptor( + repositoryDescriptor.RepositoryService, + repositoryDescriptor.RepositoryImplementation, + lifetime + ) + ); + } + + // + // DbSeeder ... + isValid = repositoryDescriptor.HasSeeder(); + if (isValid) + { + // + source.Add( + new ServiceDescriptor( + repositoryDescriptor.SeederService, + repositoryDescriptor.SeederImplementation, + lifetime + ) + ); + } + } + + /// + /// Register Repository ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXRepository< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TContext + >( + IServiceCollection source, + XEFRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TGraphSchema : Schema + where TContext : XDbContext + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TRepositoryImplementation : XBaseEFRepository + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + // + var isValid = repositoryDescriptor.IsValid(); + if (!isValid) + { + return; + } + + // + // Register KeyGenerator ... + isValid = repositoryDescriptor.HasKeyGenerator(); + if (isValid) + { + // + AddXKeyGenerator( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Events ... + isValid = repositoryDescriptor.HasEvents(); + if (isValid) + { + // + AddXEvents( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Graph ... + isValid = repositoryDescriptor.HasGraphSupport(); + if (isValid) + { + // + AddXGraphQL( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Repository ... + isValid = repositoryDescriptor.HasRepository(); + if (isValid) + { + // + source.Add( + new ServiceDescriptor( + repositoryDescriptor.RepositoryService, + repositoryDescriptor.RepositoryImplementation, + lifetime + ) + ); + } + } + + /// + /// Register Repository ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXRepository< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TContext, + TSeederService, + TSeederImplementation + >( + IServiceCollection source, + XEFRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TGraphSchema : Schema + where TContext : XDbContext + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TSeederService : IXBaseDbSeeder + where TEventsService : IXBaseRepositoryEvents + where TSeederImplementation : XBaseDbSeeder + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TRepositoryImplementation : XBaseEFRepository + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + // + var isValid = repositoryDescriptor.IsValid(); + if (!isValid) + { + return; + } + + // + // Register KeyGenerator ... + isValid = repositoryDescriptor.HasKeyGenerator(); + if (isValid) + { + // + AddXKeyGenerator( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Events ... + isValid = repositoryDescriptor.HasEvents(); + if (isValid) + { + // + AddXEvents( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Graph ... + isValid = repositoryDescriptor.HasGraphSupport(); + if (isValid) + { + // + AddXGraphQL( + source, + repositoryDescriptor, + lifetime + ); + } + + // + // Register Repository ... + isValid = repositoryDescriptor.HasRepository(); + if (isValid) + { + // + source.Add( + new ServiceDescriptor( + repositoryDescriptor.RepositoryService, + repositoryDescriptor.RepositoryImplementation, + lifetime + ) + ); + } + + // + // DbSeeder ... + isValid = repositoryDescriptor.HasSeeder(); + if (isValid) + { + // + source.Add( + new ServiceDescriptor( + repositoryDescriptor.SeederService, + repositoryDescriptor.SeederImplementation, + lifetime + ) + ); + } + } + + /// + /// Use GraphQL ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void UseXGraphQL< + TEntity, + TKey, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema + >( + IApplicationBuilder source, + string graphBasePath, + string graphPath, + bool withPlayGround = false + ) + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + // + source.UseGraphQL(graphPath); + source.UseGraphQLWebSockets(); + + // + if (withPlayGround) + { + // + source.UseGraphQLPlayground(new GraphQLPlaygroundOptions + { + Path = "/ui/playground", + GraphQLEndPoint = graphBasePath + }); + + // + source.UseGraphQLVoyager(new GraphQLVoyagerOptions + { + Path = "/ui/voyager", + GraphQLEndPoint = graphBasePath + }); + } + } + #endregion + + // + #region Entity Assembly Detector ... + /// + /// Extract Assemblies which Contains Entity ... + /// + /// + public static List ExtractEntityAssemblies() + { + // + var result = new List(); + + // + var allAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (var assembly in allAssemblies) + { + // + try + { + // + bool hasEntity = assembly + .GetExportedTypes() + .Any(t => t.IsXEntity()); + if (hasEntity) + { + result.Add(assembly); + } + } + catch { } + } + + // + return result; + } + + /// + /// Register All Entities using Model Builder ... + /// + /// + public static void RegisterAllEntities(this ModelBuilder modelBuilder) + { + // + // Extract Entity Container Assemblies ... + var entityAssemblies = ExtractEntityAssemblies(); + foreach(var assembly in entityAssemblies) + { + modelBuilder.ApplyConfigurationsFromAssembly(assembly); + } + } + + #endregion + + // + #region Private ... + /// + /// Register Unit of Works ... + /// + /// + /// + /// + public static void AddXUnitOfWorks( + IServiceCollection source, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TContext : XDbContext + { + // + source.Add( + new ServiceDescriptor( + typeof(IXUnitOfWorks), + typeof(XUnitOfWorks), + lifetime + ) + ); + } + + /// + /// Register Key Generator Service ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXKeyGenerator< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation + >( + IServiceCollection source, + XRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TEntity : XBaseEntity + where TKeyGeneratorService : IXKeyGenerator + where TKeyGeneratorImplementation : XKeyGenerator + { + // + // Validate ... + var isValid = + !source.IsNull() && + !repositoryDescriptor.IsNull() && + !repositoryDescriptor.KeyGeneratorService.IsNull() && + !repositoryDescriptor.KeyGeneratorImplementation.IsNull(); + if (!isValid) + { + return; + } + + // + // Register ... + source.Add( + new ServiceDescriptor( + repositoryDescriptor.KeyGeneratorService, + repositoryDescriptor.KeyGeneratorImplementation, + lifetime + ) + ); + } + + /// + /// Register Events Service ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXEvents< + TEntity, + TKey, + TEventsService, + TEventsImplementation + >( + IServiceCollection source, + XRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TEntity : XBaseEntity + where TEventsService : IXBaseRepositoryEvents + where TEventsImplementation : XBaseRepositoryEvents + { + // + // Validate ... + var isValid = + !source.IsNull() && + !repositoryDescriptor.IsNull() && + !repositoryDescriptor.EventsService.IsNull() && + !repositoryDescriptor.EventsImplementation.IsNull(); + if (!isValid) + { + return; + } + + // + // Register ... + source.Add( + new ServiceDescriptor( + repositoryDescriptor.EventsService, + repositoryDescriptor.EventsImplementation, + lifetime + ) + ); + } + + /// + /// Register GraphQL Service ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void AddXGraphQL< + TEntity, + TKey, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema + >( + IServiceCollection source, + XRepositoryDescriptor repositoryDescriptor, + ServiceLifetime lifetime = ServiceLifetime.Scoped + ) + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + // + // Validate ... + var isValid = + !source.IsNull() && + !repositoryDescriptor.IsNull() && + !repositoryDescriptor.GraphType.IsNull() && + !repositoryDescriptor.GraphQuery.IsNull() && + !repositoryDescriptor.GraphSchema.IsNull() && + !repositoryDescriptor.GraphTypeKey.IsNull() && + !repositoryDescriptor.GraphTypeHelperService.IsNull() && + !repositoryDescriptor.GraphTypeHelperImplementation.IsNull(); + if (!isValid) + { + return; + } + + // + // Register Graph Type ... + source.Add( + new ServiceDescriptor( + lifetime: lifetime, // ServiceLifetime.Singleton, + serviceType: repositoryDescriptor.GraphType, + implementationType: repositoryDescriptor.GraphType + ) + ); + + // + // Register Graph Events ... + source.Add( + new ServiceDescriptor( + lifetime: lifetime, // ServiceLifetime.Singleton, + serviceType: typeof(XBaseGraphQLEventType), + implementationType: typeof(XBaseGraphQLEventType) + ) + ); + + // + // Register Graph Type Helper ... + source.Add( + new ServiceDescriptor( + lifetime: ServiceLifetime.Singleton, + serviceType: repositoryDescriptor.GraphTypeHelperService, + implementationType: repositoryDescriptor.GraphTypeHelperImplementation + ) + ); + + // + // Register Graph Query ... + source.Add( + new ServiceDescriptor( + lifetime: lifetime, + serviceType: repositoryDescriptor.GraphQuery, + implementationType: repositoryDescriptor.GraphQuery + ) + ); + + // + // Register Graph Schema ... + source.Add( + new ServiceDescriptor( + lifetime: lifetime, + serviceType: repositoryDescriptor.GraphSchema, + implementationType: repositoryDescriptor.GraphSchema + ) + ); + + // + source.AddScoped(); + + // + // Register Graph Type Schema ... + var builder = source.GetRegisteredService(); + if (builder.IsNull()) + { + // + Action options = x => + { + // + x.EnableMetrics = true; + x.UnhandledExceptionDelegate = context => + { + Console.WriteLine("XGraphQL Error: " + context.OriginalException.Message); + }; + }; + builder = source.AddGraphQL(options) + .AddNewtonsoftJson(deserializerSettings => { }, serializerSettings => { }) + .AddWebSockets() + .AddDataLoader() + .AddGraphTypes(lifetime); + } + builder.AddGraphTypes( + serviceLifetime: lifetime, + typeFromAssembly: repositoryDescriptor.GraphSchema + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Helpers\XGraphQLHelper.cs + +using GraphQL.Types; +using xDataService.Models; +using xModels.Dtos; + +namespace xDataService.Constants { + public static class XGraphQLHelper { + public static QueryArgument GetIdArgument () + where TKey : IGraphType { + return new QueryArgument { + Name = "id", + Description = "the id of entity, which you want to retrieve ..." + }; + } + + public static QueryArgument IgnoreSoftDeletedArgument = new QueryArgument { + Name = "ignoreSoftDeleteds", + DefaultValue = true, + Description = "determines result should ignore soft deleted items or not ..." + }; + + public static QueryArgument SearchQueryArgument = new QueryArgument { + Name = "searchQuery", + Description = "the value which had to looking for ..." + }; + + public static QueryArgument QueryArgument = new QueryArgument { + Name = "query", + DefaultValue = new XQuery (), + Description = "determines query structure info for retrieving data ..." + }; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXBaseDbSeeder.cs + +using System.Threading.Tasks; +using xDataService.Configuration; +using xModels.Base; + +namespace xDataService.Interfaces +{ + /// + /// Db Seeder used to seed data on Database on startup time ... + /// + public interface IXBaseDbSeeder + where TEntity : XBaseEntity + { + /// + /// Holds Entity Name ... + /// + string Entity { get; } + + /// + /// Holds Database Name ... + /// + string Database { get; } + + /// + /// Repository Service for Data Manipulation ... + /// + IXBaseRepository Repository { get; } + + /// + /// DataServiceConfiguration ... + /// + XDataServiceConfiguration DataServiceConfiguration { get; } + + /// + /// do all seeding action by helping of Repositories here ... + /// + /// + Task Seed(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXBaseGraphQLQuery.cs + +using GraphQL.Types; +using xModels.Base; + +namespace xDataService.Interfaces +{ + public interface IXBaseGraphQLQuery + where TEntity : XBaseEntity + where TGraph : IGraphType + where TGraphKey : IGraphType + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXBaseGraphQLTypeHelper.cs + +using xModels.Base; + +namespace xDataService.Interfaces +{ + public interface IXBaseGraphQLTypeHelper + where TEntity : XBaseEntity + { + string GetInQuerySingleName(); + + string GetInQueryCollectionName(); + + string GetFindOneName(); + + string GetFindManyName(); + + string GetQueryName(); + + string GetCountName(); + + string GetExistsName(); + + string GetGraphQLPath(string baseGraphPath = null); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXBaseReferencedProvider.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xDataService.Models; +using xModels.Dtos; + +namespace xDataService.Interfaces +{ + /// + /// in Some Cases we have Entities Which Can Referenced for Specified + /// Provider and Types ... + /// this interface Globaly introduce all available Actions for those ... + /// + /// TKey: type of Provider Models Keys ... + /// TModel: type of Dto which Referenced ... + /// + public interface IXBaseReferencedProvider + { + // + #region Props ... + /// + /// Provider Identifier ... + /// + string Provider { get; } + #endregion + + // + #region Referenced Actions ... + /// + /// Get Specified Indexed Reference for Specific Provided ID ... + /// + /// + /// + /// + /// + Task GetReference( + TKey providedForId, + int forIndex = 0, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists References of Specific Provided ID ... + /// + /// + /// + /// + Task> GetAllReferences( + TKey providedForId, + CancellationToken cancellationToken = default + ); + + /// + /// Extract all Referencing Models for Specified Key ... + /// + /// + /// + /// + Task>> GetAllReferencings( + TKey providedForId, + CancellationToken cancellationToken = default + ); + + /// + /// Count References ... + /// + /// + /// + /// + Task CountReferences( + TKey providedForId, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve References of Specific Provided ID as Query ... + /// + /// + /// + /// + /// + Task> QueryReferences( + XQuery query, + TKey providedForId, + CancellationToken cancellationToken = default + ); + + /// + /// Add Reference a Model to Specific Provided ID ... + /// + /// + /// + /// + /// + /// + Task AddReference( + TModel model, + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Reference a Model to Specific XRefence ... + /// + /// + /// + /// + /// + /// + Task AddReference( + TModel model, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Reference a Model for Specific Provided ID ... + /// + /// + /// + /// + /// + /// + Task RemoveReference( + TModel model, + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Reference a Model for Specific XRefence ... + /// + /// + /// + /// + /// + /// + Task RemoveReference( + TModel model, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove All References to Specified Key ... + /// + /// + /// + /// + /// + Task RemoveReferences( + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXBaseRepository.cs + +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 Microsoft.EntityFrameworkCore.Query; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Interfaces +{ + public interface IXBaseRepository : IDisposable { } + + /// + /// Base Repository Pattern Contracts in XDashboard's Data Service ... + /// use for Data Manipulation ... + /// + /// + /// + public interface IXBaseRepository : IXBaseRepository + where T : XBaseEntity + { + // + #region Add ... + /// + /// add a new Entity ... + /// + /// + /// + /// + /// + Task AddAsync( + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// add or update an Entity (add if not exists/update if exists) ... + /// + /// + /// + /// + /// + Task AddOrUpdateAsync( + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// add a range of new Entities ... + /// + /// + /// + /// + /// + Task AddRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Update ... + /// + /// Update an Entity values ... + /// + /// + /// + /// + /// + /// + Task UpdateAsync( + TKey id, + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// Update a range of Entities ... + /// + /// + /// + /// + /// + Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Remove ... + /// + /// remove an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// remove an Entity ... + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + T item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// remove a range of exists Entities ... + /// + /// + /// + /// + /// + /// + Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Count ... + /// + /// count all exists Entities ... + /// + /// + /// + /// + Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// + Task PagesCountAsync( + int pageSize, + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Retrieve ... + /// + /// retrieve whole items as queryable ... + /// + /// a flag for Tracking behaviour + /// an Expression for Filter Items ... + /// an Order Builder expression for Ordering Query ... + /// an Include Builder expression for Including Navigation Properties ... + /// + IQueryable AsQueryable( + bool asNoTracking = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ); + + /// + /// retrieve an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Entities ... + /// + /// + /// + /// + /// + /// + Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Entities + /// as Async Enumerable ... + /// + /// + /// + /// + /// + IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ); + + /// + /// find an Entity by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find a collection of Entities by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve Entities based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Exists ... + /// + /// Check an Entity exists or not ... + /// + /// + /// + /// + /// + Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Unit Of Work ... + /// + /// Save all unsaved Transactions on DbContext ... + /// used fo Unit Of Works Design Pattern ... + /// + /// + /// + Task SaveChangesAsync( + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Key ... + /// + /// Retrieve Key of Entity ... + /// + /// + /// + TKey GetKey(T item); + + /// + /// Set Key of Entity ... + /// + /// + /// + void SetKey( + ref T item, + TKey id + ); + + /// + /// Handle Checking Key ... + /// + /// + /// + /// + Task HandleKeyAsync( + T item, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Detach ... + /// + /// Detach an Entity ... + /// + /// + void Detach(T item); + + /// + /// Detach an Enumerable of Entities ... + /// + /// + void Detach(IEnumerable items); + + /// + /// Detach a Query Result of Entity ... + /// + /// + void Detach(XQueryResult query); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXBaseRepositoryController.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Interfaces +{ + /// + /// an Interface for Base Repository Actions Providing Using Controllers ... + /// + /// + /// + /// + public interface IXBaseRepositoryController + where TEntity : XBaseEntity + { + // + IXBaseRepository Repository { get; } + Func, IOrderedQueryable> DefaultOrderBuilder { get; } + Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + + // + #region Add ... + Task> Add( + [FromBody] TEntity item, + CancellationToken cancellationToken = default + ); + + Task> AddOrUpdate( + [FromBody] TEntity item, + CancellationToken cancellationToken = default + ); + + Task AddMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Retrieve ... + Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Update ... + Task> Update( + [FromRoute] TKey id, + [FromBody] TEntity item, + CancellationToken cancellationToken = default + ); + + Task> UpdateMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Exists ... + Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Remove ... + Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ); + + Task RemoveMany( + [FromBody] XBaseRangeRequest request, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXBaseRepositoryEvents.cs + +using System; +using System.Collections.Generic; +using xDataService.Models; + +namespace xDataService.Interfaces { + public interface IXBaseRepositoryEvents { + // + void AddEvent (XBaseEventModel model); + void UpdateEvent (XBaseEventModel model); + void RemoveEvent (XBaseEventModel model); + + // + void AddManyEvent (XBaseEventModel> model); + void UpdateManyEvent (XBaseEventModel> model); + void RemoveManyEvent (XBaseEventModel> model); + + // + IObservable> OnAddObservable { get; } + IObservable> OnUpdateObservable { get; } + IObservable> OnRemoveObservable { get; } + + // + IObservable>> OnAddManyObservable { get; } + IObservable>> OnUpdateManyObservable { get; } + IObservable>> OnRemoveManyObservable { get; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXBaseRepositoryService.cs + +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 AutoMapper; +using Microsoft.EntityFrameworkCore.Query; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Interfaces +{ + /// + /// an Interface for Manipulating Data Using Services based on Dto Mapping ... + /// + /// + /// + /// + public interface IXBaseRepositoryService : IDisposable + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + #region Properties ... + /// + /// Mapper Instance ... + /// + IMapper Mapper { get; } + + /// + /// Repository Implementation for Data Manipulations ... + /// + IXBaseRepository Repository { get; } + + /// + /// Mapping Profile Configuration ... + /// + /// this Configuration used for Mapping TEntity to Dto and reverse ... + /// default mapping prepared on Constructing time, other custom maps + /// must handled manually if required ... + /// + MapperConfiguration MapperConfiguration { get; } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// add a new Dto ... + /// + /// + /// + /// + /// + Task AddAsync( + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// add or update a Dto (add if not exists/update if exists) ... + /// + /// + /// + /// + /// + Task AddOrUpdateAsync( + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// add a range of new Dtos ... + /// + /// + /// + /// + /// + Task AddRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Update ... + /// + /// Update an Dto values ... + /// + /// + /// + /// + /// + /// + Task UpdateAsync( + TKey id, + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// Update a range of Dtos ... + /// + /// + /// + /// + /// + Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Remove ... + /// + /// remove an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// remove an Dto ... + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + TDto item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// remove a range of exists Dtos ... + /// + /// + /// + /// + /// + /// + Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Count ... + /// + /// count all exists Dtos ... + /// + /// + /// + /// + Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// + Task PagesCountAsync( + int pageSize, + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Retrieve ... + /// + /// retrieve an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Dtos ... + /// + /// + /// + /// + /// + /// + Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Dtos + /// as Async Enumerable ... + /// + /// + /// + /// + /// + /// + IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find an Dto by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find a collection of Dtos by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve Dtos based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Exists ... + /// + /// Check a Dto exists or not ... + /// + /// + /// + /// + /// + Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + #endregion + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXBaseServiceController.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Interfaces +{ + /// + /// an Interface for Base RepositoryService Actions Providing Using Controllers ... + /// + /// + /// + /// + public interface IXBaseServiceController + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + IXBaseRepositoryService RepositoryService { get; } + Func, IOrderedQueryable> DefaultOrderBuilder { get; } + Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + + // + #region Add ... + Task> Add( + TDto item, + CancellationToken cancellationToken = default + ); + + Task> AddOrUpdate( + TDto item, + CancellationToken cancellationToken = default + ); + + Task AddMany( + XBaseRangeRequest request, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Retrieve ... + Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Update ... + Task> Update( + [FromRoute] TKey id, + [FromBody] TDto item, + CancellationToken cancellationToken = default + ); + + Task> UpdateMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Exists ... + Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeletedss = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Remove ... + Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ); + + Task RemoveMany( + [FromBody] XBaseRangeRequest request, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXDatabaseDescriptor.cs + +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson.Serialization.Conventions; +using xDataService.Configuration; +using xDataService.Constants; +using xDataService.Db; +using xDataService.Models; + +namespace xDataService.Interfaces +{ + /// + /// Data Base Descriptor ... + /// + public interface IXDatabaseDescriptor + { + // + #region Props ... + /// + /// Database Name ... + /// + /// + string Name { get; set; } + + /// + /// Database Provider ... + /// + /// + XDbProviders Provider { get; set; } + + /// + /// Db Context Type ... + /// + /// + Type Context { get; set; } + + /// + /// Database Connecion String ... + /// + /// + string ConnectionString { get; set; } + + /// + /// Data Service Configuration ... + /// + /// + XDataBaseConfiguration Configuration { get; set; } + + /// + /// Repositories Descriptions ... + /// + /// + IList Repositories { get; set; } + + /// + /// Mongo Convention Packs ... + /// + ConventionPack ConventionPacks { get; set; } + + /// + /// DbContext Options Builder ... + /// + /// + Action OptionsBuilder { get; set; } + #endregion + + // + #region Configure ... + /// + /// Configure Database Descriptor using Registered Services ... + /// + /// + void Configure( + IServiceCollection services + ); + + /// + /// Configure Database Descriptor using IConfiguration ... + /// + /// + void Configure( + IConfiguration configuration + ); + + /// + /// Configure Database Descriptor using Database Configuration ... + /// + /// + void Configure( + XDataBaseConfiguration configuration + ); + + /// + /// Configure Database Descriptor using Provider and Connection String ... + /// + /// + /// + void Configure( + XDbProviders provider, + string connectionString + ); + #endregion + } + + /// + /// Data Base Descriptor ... + /// + /// + public interface IXDatabaseDescriptor : IXDatabaseDescriptor + where TContext : XDbContext + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXEntityRegisterar.cs + +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using xModels.Base; + +namespace xDataService.Interfaces +{ + /// + /// an Interface to Intruduce Entities for Dynamic Registration ... + /// + public interface IXEntityRegisterar + { + /// + /// Database Name ... + /// + string Name { get; } + + /// + /// Returns all entity types this plugin wants registered in the DbContext + /// + IEnumerable GetEntityTypes(); + + /// + /// Optional: Apply additional per-entity configuration + /// + void ConfigureEntities(ModelBuilder modelBuilder); + + /// + /// Add XDataService Based Entity to List ... + /// + /// + /// + void AddEntity() + where TEntity : XBaseEntity; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXKeyGenerator.cs + +using System.Threading; +using System.Threading.Tasks; +using xModels.Base; + +namespace xDataService.Interfaces +{ + /// + /// Typed Base Key Generator ... + /// + /// + /// + public interface IXKeyGenerator + where TEntity : XBaseEntity + { + bool IsEmpty(TKey id); + Task GenerateKey( + IXBaseRepository repository, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXSequentialGuid.cs + +using System; + +namespace xDataService.Interfaces { + /// + /// this service provide Sequential GUID mechanism for Entity Ids ... + /// + public interface IXSequentialGuid { + Guid GetCurrentGuid (); + Guid Next (); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Interfaces\IXUnitOfWorks.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using xDataService.Db; +using xModels.Base; + +namespace xDataService.Interfaces { + /// + /// Provide Unit Of Works Design Pattern for Db Transactions ... + /// Only Used on EFCore ... + /// + /// + public interface IXUnitOfWorks : IDisposable + where TDbContext : XDbContext { + TDbContext DbContext { get; } + DbSet GetDbSet () where T : XBaseEntity; + int SaveChanges (); + Task SaveChangesAsync ( + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Models\XBaseEventModel.cs + +namespace xDataService.Models { + /// + /// Pass Data on Repository Events ... + /// + /// + public class XBaseEventModel { + public T Model { get; set; } + + public XBaseEventModel (T model) { + Model = model; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Models\XDatabaseDescriptor.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson.Serialization.Conventions; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Constants; +using xDataService.Db; +using xDataService.DI; +using xDataService.Interfaces; + +namespace xDataService.Models +{ + /// + /// Data Base Descriptor ... + /// + public abstract class XDatabaseDescriptor : IXDatabaseDescriptor + { + // + #region Props ... + /// + /// Database Name ... + /// + /// + public virtual string Name { get; set; } + + /// + /// Database Provider ... + /// + /// + public virtual XDbProviders Provider { get; set; } + + /// + /// Db Context... + /// + /// + public virtual Type Context { get; set; } = null; + + /// + /// Database Connecion String ... + /// + /// + public virtual string ConnectionString { get; set; } + + /// + /// Data Service Configuration ... + /// + /// + public virtual XDataBaseConfiguration Configuration { get; set; } + + /// + /// Repositories Descriptions ... + /// + /// + public virtual IList Repositories { get; set; } = null; + + /// + /// Mongo Convention Packs ... + /// + public ConventionPack ConventionPacks { get; set; } = null; + + /// + /// DbContext Options Builder ... + /// + /// + public Action OptionsBuilder { get; set; } = null; + #endregion + + // + #region Constructor ... + /// + /// Constructor of Descriptor ... + /// + /// + /// + /// + public XDatabaseDescriptor( + string name, + IList repositories = null + ) + { + // + Name = name; + Repositories = repositories; + } + #endregion + + // + #region Configure ... + /// + /// Configure Database Descriptor using Registered Services ... + /// + /// + public void Configure( + IServiceCollection services + ) + { + // + bool isValid = !Name.IsNullOrEmpty(); + if (!isValid) + { + return; + } + + // + var xDataServiceConfiguration = services.GetRegisteredService(); + isValid = + !xDataServiceConfiguration.IsNullOrDefault() && + xDataServiceConfiguration.Databases.HasChild() && + xDataServiceConfiguration.Databases.Keys.Any(k => k == Name); + if (!isValid) + { + return; + } + + // + var database = xDataServiceConfiguration.Databases[Name]; + isValid = !database.IsNullOrDefault(); + if (!isValid) + { + return; + } + + // + Configure(database); + } + + /// + /// Configure Database Descriptor using IConfiguration ... + /// + /// + public void Configure( + IConfiguration configuration + ) + { + // + bool isValid = !Name.IsNullOrEmpty(); + if (!isValid) + { + return; + } + + // + var xDataServiceConfiguration = configuration.GetXDataServiceConfiguration(); + isValid = + !xDataServiceConfiguration.IsNullOrDefault() && + xDataServiceConfiguration.Databases.HasChild() && + xDataServiceConfiguration.Databases.Keys.Any(k => k == Name); + if (!isValid) + { + return; + } + + // + var database = xDataServiceConfiguration.Databases[Name]; + isValid = !database.IsNullOrDefault(); + if (!isValid) + { + return; + } + + // + Configure(database); + } + + /// + /// Configure Database Descriptor using Database Configuration ... + /// + /// + public void Configure( + XDataBaseConfiguration configuration + ) + { + // + Configuration = configuration; + Provider = configuration.Provider; + ConnectionString = configuration.ConnectionString; + } + + /// + /// Configure Database Descriptor using Provider and Connection String ... + /// + /// + /// + public void Configure( + XDbProviders provider, + string connectionString + ) + { + // + Provider = provider; + ConnectionString = connectionString; + Configuration = new XDataBaseConfiguration + { + Provider = provider, + ConnectionString = connectionString + }; + } + #endregion + } + + /// + /// Data Base Descriptor ... + /// + /// + public abstract class XDatabaseDescriptor : XDatabaseDescriptor, IXDatabaseDescriptor + where TContext : XDbContext + { + /// + /// Constructor of Descriptor ... + /// + /// + /// + /// + /// + public XDatabaseDescriptor( + string name, + IList repositories = null + ) : base( + name: name, + repositories: repositories + ) + { + Context = typeof(TContext); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Models\XGraphQueryResult.cs + +using GraphQL.Types; +using xModels.Dtos; + +namespace xDataService.Models { + public class XGraphQueryResult : ObjectGraphType> + where TGraph : IGraphType { + public XGraphQueryResult () { + // + Name = $"{typeof(TGraph).Name}QueryResult"; + + // + Field> (nameof (XQueryResult.Items)); + Field (x => x.Page); + Field (x => x.PageSize); + Field (x => x.TotalPages); + Field (x => x.TotalFilteredItems); + Field (x => x.TotalItems); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Models\XGraphQueryType.cs + +using GraphQL.Types; +using xModels.Dtos; + +namespace xDataService.Models { + public class XGraphQueryType : InputObjectGraphType { + public XGraphQueryType () { + // + Name = "query"; + + // + Field (x => x.Filter); + Field (x => x.ContainsDetail); + Field (x => x.SortBy); + Field (x => x.IsAscending); + Field (x => x.Page); + Field (x => x.PageSize); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Models\XReference.cs + +namespace xDataService.Models +{ + public class XReference + { + public int Index { get; set; } + public T ReferencedTo { get; set; } + public string ProvidedFor { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Models\XRepositoryDescriptor.cs + +using System; +using GraphQL.Types; +using xDataService.Db; +using xDataService.GraphQL; +using xDataService.Interfaces; +using xDataService.Providers; +using xModels.Base; + +namespace xDataService.Models +{ + /// + /// a Global non Generic Repository Descriptor ... + /// + public class XRepositoryDescriptor + { + /// + /// Entity Key Type ... + /// + /// + public Type Key { get; set; } = null; + + /// + /// Entity Type ... + /// + /// + public Type Entity { get; set; } = null; + + /// + /// Context Type ... + /// + /// + public Type Context { get; set; } = null; + + /// + /// Graph Type Key ... + /// + /// + public Type GraphTypeKey { get; set; } = null; + + /// + /// Graph Type ... + /// + /// + public Type GraphType { get; set; } = null; + + /// + /// Entity Type GraphQL Schema ... + /// + /// + public Type GraphSchema { get; set; } = null; + + /// + /// Entity GraphQL Type Helper ... + /// + /// + public Type GraphTypeHelperService { get; set; } = null; + + /// + /// Entity GraphQL Type Helper ... + /// + /// + public Type GraphTypeHelperImplementation { get; set; } = null; + + /// + /// Entity GrapQL Query ... + /// + /// + public Type GraphQuery { get; set; } = null; + + /// + /// Repository Event Provider ... + /// + /// + public Type EventsService { get; set; } = null; + + /// + /// Repository Event Provider ... + /// + /// + public Type EventsImplementation { get; set; } = null; + + /// + /// Repository Key Generator for Entity ... + /// + /// + public Type KeyGeneratorService { get; set; } = null; + + /// + /// Repository Key Generator for Entity ... + /// + /// + public Type KeyGeneratorImplementation { get; set; } = null; + + /// + /// Repository Data Access Design Pattern ... + /// + /// + public Type RepositoryService { get; set; } = null; + + /// + /// Repository Data Access Design Pattern ... + /// + /// + public Type RepositoryImplementation { get; set; } = null; + + /// + /// Database Data Seeder ... + /// + public Type SeederService { get; set; } = null; + + /// + /// Database Data Seeder ... + /// + public Type SeederImplementation { get; set; } = null; + } + + /// + /// a Repository Descriptor ... + /// + /// + /// + public class XRepositoryDescriptor : XRepositoryDescriptor + where TEntity : XBaseEntity + { + public XRepositoryDescriptor() : base() + { + // + Key = typeof(TKey); + Entity = typeof(TEntity); + } + } + + /// + /// a Repository Descriptor ... + /// + public class XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation + > : XRepositoryDescriptor + where TEntity : XBaseEntity + where TKeyGeneratorService : IXKeyGenerator + where TKeyGeneratorImplementation : XKeyGenerator + { + public XRepositoryDescriptor() : base() + { + // + KeyGeneratorService = typeof(TKeyGeneratorService); + KeyGeneratorImplementation = typeof(TKeyGeneratorImplementation); + } + } + + /// + /// a Repository Descriptor ... + /// + public class XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation + > : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation + > + where TEntity : XBaseEntity + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + { + public XRepositoryDescriptor() : base() + { + // + EventsService = typeof(TEventsService); + EventsImplementation = typeof(TEventsImplementation); + } + } + + /// + /// a Repository Descriptor ... + /// + public class XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema + > : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation + > + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + public XRepositoryDescriptor() : base() + { + // + GraphType = typeof(TGraph); + GraphTypeKey = typeof(TGraphKey); + GraphQuery = typeof(TGraphQuery); + GraphSchema = typeof(TGraphSchema); + GraphTypeHelperService = typeof(TGraphTypeHelperService); + GraphTypeHelperImplementation = typeof(TGraphTypeHelperImplementation); + } + } + + /// + /// a Repository Descriptor ... + /// + public class XInMemoryRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation + > : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema + > + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TRepositoryImplementation : XBaseInMemoryRepository + where TGraphQuery : XBaseGraphQLQuery + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + public XInMemoryRepositoryDescriptor() : base() + { + // + RepositoryService = typeof(TRepositoryService); + RepositoryImplementation = typeof(TRepositoryImplementation); + } + } + + /// + /// a Repository Descriptor ... + /// + public class XInMemoryRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TSeederService, + TSeederImplementation + > : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema + > + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TSeederService : IXBaseDbSeeder + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TSeederImplementation : XBaseDbSeeder + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TRepositoryImplementation : XBaseInMemoryRepository + where TGraphQuery : XBaseGraphQLQuery + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + public XInMemoryRepositoryDescriptor() : base() + { + // + SeederService = typeof(TSeederService); + SeederImplementation = typeof(TSeederImplementation); + + // + RepositoryService = typeof(TRepositoryService); + RepositoryImplementation = typeof(TRepositoryImplementation); + } + } + + /// + /// a Repository Descriptor ... + /// + public class XMongoRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation + > : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema + > + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TRepositoryImplementation : XBaseMongoRepository + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + public XMongoRepositoryDescriptor() : base() + { + // + RepositoryService = typeof(TRepositoryService); + RepositoryImplementation = typeof(TRepositoryImplementation); + } + } + + /// + /// a Repository Descriptor ... + /// + public class XMongoRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TSeederService, + TSeederImplementation + > : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema + > + where TGraphSchema : Schema + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TSeederService : IXBaseDbSeeder + where TEventsService : IXBaseRepositoryEvents + where TSeederImplementation : XBaseDbSeeder + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TRepositoryImplementation : XBaseMongoRepository + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + public XMongoRepositoryDescriptor() : base() + { + // + SeederService = typeof(TSeederService); + SeederImplementation = typeof(TSeederImplementation); + + // + RepositoryService = typeof(TRepositoryService); + RepositoryImplementation = typeof(TRepositoryImplementation); + } + } + + /// + /// a Repository Descriptor ... + /// + public class XEFRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TContext + > : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema + > + where TGraphSchema : Schema + where TContext : XDbContext + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TEventsService : IXBaseRepositoryEvents + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TRepositoryImplementation : XBaseEFRepository + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + public XEFRepositoryDescriptor() : base() + { + // + Context = typeof(TContext); + RepositoryService = typeof(TRepositoryService); + RepositoryImplementation = typeof(TRepositoryImplementation); + } + } + + /// + /// a Repository Descriptor ... + /// + public class XEFRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema, + TRepositoryService, + TRepositoryImplementation, + TContext, + TSeederService, + TSeederImplementation + > : XRepositoryDescriptor< + TEntity, + TKey, + TKeyGeneratorService, + TKeyGeneratorImplementation, + TEventsService, + TEventsImplementation, + TGraph, + TGraphKey, + TGraphQuery, + TGraphTypeHelperService, + TGraphTypeHelperImplementation, + TGraphSchema + > + where TGraphSchema : Schema + where TContext : XDbContext + where TGraphKey : IGraphType + where TEntity : XBaseEntity + where TGraph : XBaseGraphObjectType + where TSeederService : IXBaseDbSeeder + where TEventsService : IXBaseRepositoryEvents + where TSeederImplementation : XBaseDbSeeder + where TKeyGeneratorService : IXKeyGenerator + where TRepositoryService : IXBaseRepository + where TEventsImplementation : XBaseRepositoryEvents + where TKeyGeneratorImplementation : XKeyGenerator + where TGraphTypeHelperService : IXBaseGraphQLTypeHelper + where TGraphQuery : XBaseGraphQLQuery + where TRepositoryImplementation : XBaseEFRepository + where TGraphTypeHelperImplementation : XBaseGraphQLTypeHelper + { + public XEFRepositoryDescriptor() : base() + { + // + SeederService = typeof(TSeederService); + SeederImplementation = typeof(TSeederImplementation); + + // + Context = typeof(TContext); + RepositoryService = typeof(TRepositoryService); + RepositoryImplementation = typeof(TRepositoryImplementation); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Mongo\XAsyncEnumerableAdapter.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Driver; + +namespace xDataService.Mongo { + public class XAsyncEnumerableAdapter : IAsyncEnumerable { + // + private readonly IAsyncCursorSource asyncCursorSource; + + // + public XAsyncEnumerableAdapter (IAsyncCursorSource asyncCursorSource) { + this.asyncCursorSource = asyncCursorSource; + } + + // + public IAsyncEnumerator GetAsyncEnumerator (CancellationToken cancellationToken) => + new XAsyncEnumeratorAdapter (asyncCursorSource, cancellationToken); + } + + public class XAsyncEnumeratorAdapter : IAsyncEnumerator { + private readonly IAsyncCursorSource asyncCursorSource; + private readonly CancellationToken cancellationToken; + private IAsyncCursor asyncCursor; + private IEnumerator batchEnumerator; + + public T Current => batchEnumerator.Current; + + public XAsyncEnumeratorAdapter ( + IAsyncCursorSource asyncCursorSource, + CancellationToken cancellationToken + ) { + // + this.asyncCursorSource = asyncCursorSource; + this.cancellationToken = cancellationToken; + } + + public async ValueTask MoveNextAsync () { + // + if (asyncCursor == null) { + asyncCursor = await asyncCursorSource + .ToCursorAsync (cancellationToken); + } + + // + if (batchEnumerator != null && + batchEnumerator.MoveNext ()) { + return true; + } + + // + if (asyncCursor != null && + await asyncCursor.MoveNextAsync (cancellationToken)) { + // + batchEnumerator?.Dispose (); + batchEnumerator = asyncCursor.Current.GetEnumerator (); + + // + return batchEnumerator.MoveNext (); + } + + // + return false; + } + + public async ValueTask DisposeAsync () { + // + await Task.CompletedTask; + asyncCursor?.Dispose (); + asyncCursor = null; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Mongo\XMongoAsyncCursor.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Driver; + +namespace xDataService.Mongo { + public class XMongoAsyncCursor : IAsyncCursor { + public IEnumerable Current { get; } + private IEnumerator CurrentEnumerator { get; } + + public XMongoAsyncCursor (IEnumerable source) { + this.Current = source; + this.CurrentEnumerator = source.GetEnumerator (); + } + + public void Dispose () { + CurrentEnumerator.Dispose (); + } + + public bool MoveNext (CancellationToken cancellationToken = default) { + return CurrentEnumerator.MoveNext (); + } + + public Task MoveNextAsync (CancellationToken cancellationToken = default) { + return Task.Run ( + () => CurrentEnumerator.MoveNext (), + cancellationToken : cancellationToken + ); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Mongo\XMongoQueryable.cs + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using xModels.Base; + +namespace xDataService.Mongo { + public class XMongoQueryable : IMongoQueryable + where TEntity : XBaseEntity { + private readonly IMongoCollection collection; + + public Type ElementType { get; } + public IQueryable Items { get; } + public Expression Expression { get; } + public IQueryProvider Provider { get; } + + public XMongoQueryable ( + IQueryable items, + IMongoCollection collection + ) { + // + this.Items = items; + this.collection = collection; + + // + Provider = items.Provider; + Expression = items.Expression; + ElementType = items.ElementType; + } + + public IEnumerator GetEnumerator () { + return Items.GetEnumerator (); + } + + public QueryableExecutionModel GetExecutionModel () { + return collection.AsQueryable ().GetExecutionModel (); + } + + public IAsyncCursor ToCursor (CancellationToken cancellationToken = default) { + return new XMongoAsyncCursor (Items); + } + + public Task> ToCursorAsync (CancellationToken cancellationToken = default) { + return Task.Run (() => new XMongoAsyncCursor (Items) as IAsyncCursor); + } + + IEnumerator IEnumerable.GetEnumerator () { + return Items.GetEnumerator (); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Mongo\XMongoReadPreferenceResolver.cs + +using MongoDB.Driver; + +namespace xDataService.Mongo { + public static class XMongoReadPreferenceResolver { + public static ReadPreference GetEffectiveReadPreference ( + IClientSessionHandle session, + ReadPreference explicitReadPreference, + ReadPreference defaultReadPreference) { + if (explicitReadPreference != null) { + return explicitReadPreference; + } + + if (session.IsInTransaction) { + var transactionReadPreference = session.WrappedCoreSession.CurrentTransaction.TransactionOptions.ReadPreference; + if (transactionReadPreference != null) { + return transactionReadPreference; + } + } + + return defaultReadPreference ?? ReadPreference.Primary; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XBaseDbSeeder.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Constants; +using xDataService.Interfaces; +using xModels.Base; + +namespace xDataService.Providers +{ + public abstract class XBaseDbSeeder : IXBaseDbSeeder + where TEntity : XBaseEntity + { + // + #region Properties ... + /// + /// Holds Entity Name ... + /// + public string Entity { get; } + + /// + /// Holds Database Name ... + /// + public string Database { get; } + + /// + /// Repository Service for Data Manipulation ... + /// + public IXBaseRepository Repository { get; } + + /// + /// DataServiceConfiguration ... + /// + public XDataServiceConfiguration DataServiceConfiguration { get; } + #endregion + + // + #region Constructor ... + public XBaseDbSeeder( + string entity, + string database, + IXBaseRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) + { + // + this.Entity = entity; + this.Database = database; + this.Repository = repository; + this.DataServiceConfiguration = dataServiceConfiguration; + } + #endregion + + public async Task Seed() + { + // + // Validate Seed ... + var isValid = + DataServiceConfiguration.SeedingMode != XDbSeedingMode.None && + DataServiceConfiguration + .Databases + .Any(db => + db.Key == Database && + db.Value.SeedItems + .Any(si => + si.Key == Entity && + si.Value.HasChild())); + if (!isValid) + { + return; + } + + // + // Extract Seeding Items ... + var seedItems = DataServiceConfiguration + .Databases[Database] + .SeedItems[Entity] + .ToList(); + foreach (var item in seedItems) + { + // + try + { + // + var entity = item + .ToJSON() + .FromJSON(); + + // + // Check Item Exists or not ... + var isExist = false; + TEntity exists = null; + var asyncEnumerable = this.Repository.GetAllAsAsyncEnumerable(); + await foreach (var e in asyncEnumerable) + { + // + isExist = e.IsSameContent( + dest: entity, + propertyBlackList: new List + { + nameof(entity.Id), + nameof(entity.Deleted) + }); + if (isExist) + { + exists = e; + break; + } + } + + // + // Seed Based on Seed Mode ... + var seedMode = DataServiceConfiguration.SeedingMode; + switch (seedMode) + { + // + case XDbSeedingMode.AddIfNotExists: + // + if (!isExist) + { + // + exists = await Repository.AddAsync( + item: entity, + saveChanges: true + ); + } + break; + + // + case XDbSeedingMode.AddOrUpdate: + // + exists = exists.UpdateData( + updateWith: entity, + propertyBlackList: new List + { + nameof(entity.Id), + nameof(entity.Deleted) + } + ); + + // + await Repository.AddOrUpdateAsync(exists); + break; + } + } + catch + { } + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XBaseEFRepository.cs + +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 Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Query; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Extensions; +using xDataService.Interfaces; +using xDataService.Models; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Providers +{ + /// + /// a Base Repository Pattern Implementation Specially for EF DBs using + /// EF Core Capabilities ... + /// + /// + /// + /// + public abstract class XBaseEFRepository : IXBaseRepository + where T : XBaseEntity + where TDbContext : XDbContext + { + // + #region Properties ... + // + public readonly DbSet dbSet; + private readonly IXKeyGenerator keyGenerator; + public readonly IXUnitOfWorks unitOfWorks; + public readonly XDataServiceConfiguration configuration; + private readonly IXBaseRepositoryEvents baseRepositoryEvents; + #endregion + + // + #region Constructor ... + public XBaseEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXKeyGenerator keyGenerator = null, + IXBaseRepositoryEvents baseRepositoryEvents = null + ) + { + // + this.unitOfWorks = unitOfWorks; + this.keyGenerator = keyGenerator; + this.configuration = configuration; + this.dbSet = unitOfWorks.GetDbSet(); + this.baseRepositoryEvents = baseRepositoryEvents; + } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// add a new Entity ... + /// + /// + /// + /// + /// + public async Task AddAsync( + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Handle Key ... + item = await HandleKeyAsync( + item: item, + cancellationToken: cancellationToken + ); + + // + var entry = await dbSet.AddAsync( + entity: item, + cancellationToken: cancellationToken + ); + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .AddEvent(new XBaseEventModel(entry.Entity)); + } + + // + // Return result base on action Succeed ... + return isSucceed ? + entry.Entity : + null; + } + + /// + /// add or update an Entity (add if not exists/update if exists) ... + /// + /// + /// + /// + /// + public async Task AddOrUpdateAsync( + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isExists = await IsExistsAsync( + id: GetKey(item), + cancellationToken: cancellationToken + ); + if (!isExists) + { + // + return await AddAsync( + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + else + { + // + return await UpdateAsync( + item: item, + id: GetKey(item), + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + } + + /// + /// add a range of new Entities ... + /// + /// + /// + /// + /// + public async Task AddRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Preparing Ids and Keys ... + await dbSet.AddRangeAsync( + await Task + .WhenAll( + items + .Select(async i => await HandleKeyAsync(i)) + ), + cancellationToken: cancellationToken + ); + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .AddManyEvent(new XBaseEventModel>(null)); + } + } + #endregion + + // + #region Update ... + /// + /// Update an Entity values ... + /// + /// + /// + /// + /// + /// + public async Task UpdateAsync( + TKey id, + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Get Exists Entity ... + var existsEntity = await GetAsync( + id: id, + ignoreSoftDeleteds: false, + cancellationToken: cancellationToken + ); + + // + // Update Data ... + existsEntity.UpdateData(item); + var entry = dbSet.Attach(existsEntity); + entry.State = EntityState.Modified; + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .UpdateEvent(new XBaseEventModel(entry.Entity)); + } + + // + // Return result base on action Succeed ... + return isSucceed ? + entry.Entity : + null; + } + + /// + /// Update a range of Entities ... + /// + /// + /// + /// + /// + public async Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + items + .ToList() + .ForEach(i => + { + var entry = dbSet.Attach(i); + entry.State = EntityState.Modified; + }); + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .UpdateManyEvent(new XBaseEventModel>(null)); + } + + // + // Return result base on action Succeed ... + return isSucceed; + } + #endregion + + // + #region Remove ... + /// + /// remove an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Retrieve Item ... + var item = await GetAsync( + id, + ignoreSoftDeleteds: false, + cancellationToken: cancellationToken + ); + if (item.IsNullOrDefault()) + { + return null; + } + + // + // Handle Remove ... + EntityEntry entry = null; + if (softDelete && configuration.EnableSoftDelete) + { + // + item.Deleted = true; + entry = dbSet.Update(item); + } + else + { + entry = dbSet.Remove(item); + } + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .RemoveEvent(new XBaseEventModel(entry.Entity)); + } + + // + // Return result base on action Succeed ... + return isSucceed ? + entry.Entity : + null; + } + + /// + /// remove an Entity ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + T item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await RemoveAsync( + id: item.Id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// remove a range of exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Handle Remove ... + if (softDelete && configuration.EnableSoftDelete) + { + // + foreach (var item in items) + { + item.Deleted = true; + } + + // + dbSet.UpdateRange(items); + } + else + { + dbSet.RemoveRange(items); + } + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .RemoveManyEvent(new XBaseEventModel>(null)); + } + } + #endregion + + // + #region Count ... + /// + /// count all exists Entities ... + /// + /// + /// + /// + public async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = 0; + + // + if (ignoreSoftDeleteds) + { + // + result = await dbSet.CountAsync( + predicate: x => !x.Deleted, + cancellationToken: cancellationToken + ); + } + else + { + result = await dbSet.CountAsync(cancellationToken); + } + + // + return result; + } + + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// + public async Task PagesCountAsync( + int pageSize, + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + int count = totalItems.HasValue ? totalItems.Value : await CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + int pagesCount = count / pageSize; + + // + if (count % pageSize > 0) + { + pagesCount++; + } + + // + return pagesCount; + } + #endregion + + // + #region Retrieve ... + /// + /// retrieve whole items as queryable ... + /// + /// a flag for Tracking behaviour + /// an Expression for Filter Items ... + /// an Order Builder expression for Ordering Query ... + /// an Include Builder expression for Including Navigation Properties ... + /// + public IQueryable AsQueryable( + bool asNoTracking = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + var result = dbSet + .AsQueryable(); + + // + // Apply Predicate ... + if (!predicate.IsNull()) + { + result = result + .Where(predicate); + } + + // + // Apply Includes ... + if (!includeBuilder.IsNull()) + { + result = includeBuilder(result); + } + + // + // Apply Ordering ... + if (!orderBuilder.IsNull()) + { + result = orderBuilder(result); + } + + // + return result; + } + + /// + /// retrieve an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = (await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .ToListAsync(cancellationToken)) + .FirstOrDefault(x => GetKey(x).ToString() == id.ToString()); + + // + return result; + } + + /// + /// retrieve all exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities + /// as Async Enumerable ... + /// + /// + /// + /// + /// + public IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .AsAsyncEnumerable(); + + // + return result; + + } + + /// + /// find an Entity by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = (await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .ToListAsync(cancellationToken)) + .FirstOrDefault(predicate.Compile()); + + // + return result; + } + + /// + /// find a collection of Entities by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = (await AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .ToListAsync(cancellationToken)) + .Where(predicate.Compile()); + + // + return result; + } + + /// + /// retrieve Entities based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Normalize Query ... + query = query.NormalizeQuery(configuration); + + // + var items = await GetAllAsync(); + 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; + } + #endregion + + // + #region Exists ... + /// + /// Check an Entity exists or not ... + /// + /// + /// + /// + /// + public async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = (await AsQueryable( + asNoTracking: true, + orderBuilder: null, + includeBuilder: null, + predicate: predicator + ) + .ToListAsync(cancellationToken)) + .Any(predicate: x => GetKey(x).ToString() == id.ToString()); + + // + return result; + } + #endregion + + // + #region Unit Of Work ... + /// + /// Save all unsaved Transactions on DbContext ... + /// used fo Unit Of Works Design Pattern ... + /// + /// + /// + public async Task SaveChangesAsync( + CancellationToken cancellationToken = default + ) + { + return await unitOfWorks.SaveChangesAsync(cancellationToken); + } + #endregion + + // + #region Key ... + /// + /// Retrieve Key of Entity ... + /// + /// + /// + public TKey GetKey(T item) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + + // + var keyString = string.Empty; + if (keyProp.IsNull()) + { + keyString = string.Empty; + } + else + { + keyString = keyProp.GetValue(item).ToString(); + } + + // + if (keyString.IsNullOrEmpty()) + { + return default(TKey); + } + + // + // Prevent Deserializing issues throug JsonReader ... + if (keyString.IsGuid() && typeof(TKey) == typeof(Guid)) + { + return item.Id; + } + + // + return keyString.FromJSON(); + } + + /// + /// Set Key of Entity ... + /// + /// + /// + public void SetKey( + ref T item, + TKey id + ) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + if (keyProp.IsNull()) + { + return; + } + + // + Type t = Nullable.GetUnderlyingType(keyProp.PropertyType) ?? keyProp.PropertyType; + object safeValue = (id == null) ? null : Convert.ChangeType(id, t); + keyProp.SetValue(item, safeValue, null); + } + + /// + /// Handle Checking Key ... + /// + /// + /// + /// + public async Task HandleKeyAsync( + T item, + CancellationToken cancellationToken = default + ) + { + // + var keyType = typeof(TKey); + + // + // Handle Guid Key Type ... + // Since EFCore has AutoIncrement on int Ids, there is no need to handle int Key types ... + if ( + ( + keyType == typeof(Guid) || + keyType == typeof(string) + ) && + keyGenerator.IsEmpty(item.Id) + ) + { + // + var newKey = await keyGenerator.GenerateKey(this); + + // + SetKey(ref item, newKey); + } + + // + return item; + } + #endregion + + // + #region Detach ... + /// + /// Detach an Entity ... + /// + /// + public void Detach(T item) + { + // + if (item.IsNull()) + { + return; + } + + // + Entry(item).State = EntityState.Detached; + } + + /// + /// Detach an Enumerable of Entities ... + /// + /// + public void Detach(IEnumerable items) + { + // + if (items.IsNull() || !items.HasChild()) + { + return; + } + + // + items + .ToList() + .ForEach(item => + { + Detach(item); + }); + } + + /// + /// Detach a Query Result of Entity ... + /// + /// + public void Detach(XQueryResult query) + { + // + if (query.IsNull() || query.Items.HasChild()) + { + return; + } + + // + Detach(query.Items); + } + #endregion + #endregion + + // + #region Others ... + public void Dispose() + { + unitOfWorks.Dispose(); + } + + public string GetPropValues(T item) + { + // + var props = item.GetType().GetProperties(); + var vals = props.Select(p => p.GetValue(p.Name)); + + // + return vals.ToJSON(); + } + + public EntityEntry Entry(T item) + { + return unitOfWorks.DbContext.Entry(item); + } + #endregion + + // + #region Private ... + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XBaseEntityRegisterar.cs + +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using xDataService.Interfaces; +using xModels.Base; + +namespace xDataService.Providers +{ + /// + /// Base Entity Registerar ... + /// + public abstract class XBaseEntityRegisterar : IXEntityRegisterar + { + /// + /// Private Entity Types Collection ... + /// + private readonly List entityTypes = []; + + /// + /// Name ... + /// + public string Name { get; } + + protected XBaseEntityRegisterar(string name) + { + Name = name; + } + + /// + /// Add Specified Entity to Collection ... + /// + /// + /// + public void AddEntity() + where TEntity : XBaseEntity + { + entityTypes.Add(typeof(TEntity)); + } + + /// + /// Retrieve Registered Entity Types ... + /// + /// + public IEnumerable GetEntityTypes() + { + return entityTypes; + } + + /// + /// Configure Dynamic Entities ... + /// + /// + public virtual void ConfigureEntities(ModelBuilder modelBuilder) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XBaseEntityTypeConfiguration.cs + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xModels.Base; + +namespace xDataService.Providers +{ + /// + /// Configure an Entity Specifications ... + /// + /// + /// + public abstract class XBaseEntityTypeConfiguration : IEntityTypeConfiguration + where TEntity : XBaseEntity + { + public abstract void Configure(EntityTypeBuilder builder); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XBaseInMemoryRepository.cs + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Extensions; +using xDataService.Interfaces; +using xDataService.Models; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Providers +{ + /// + /// a Base Repository Pattern Implementation Specially for InMemory Stores using + /// EF Core Capabilities ... + /// + /// + /// + public abstract class XBaseInMemoryRepository : IXBaseRepository + where TEntity : XBaseEntity + { + // + #region Props ... + private readonly XDataServiceConfiguration configuration; + private readonly IXKeyGenerator keyGenerator; + private readonly IXBaseRepositoryEvents baseRepositoryEvents; + private static readonly ConcurrentDictionary store = new ConcurrentDictionary(); + #endregion + + // + #region Constructor ... + public XBaseInMemoryRepository( + XDataServiceConfiguration configuration, + IXKeyGenerator keyGenerator = null, + IXBaseRepositoryEvents baseRepositoryEvents = null + ) + { + this.keyGenerator = keyGenerator; + this.configuration = configuration; + this.baseRepositoryEvents = baseRepositoryEvents; + } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// add a new Entity ... + /// + /// + /// + /// + /// + public async Task AddAsync( + TEntity item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Handle Key ... + item = await HandleKeyAsync( + item: item, + cancellationToken: cancellationToken + ); + + // + var isSucceed = false; + + // + // Add Item to Dictionary ... + try + { + // + store[item.Id] = item; + isSucceed = true; + } + catch + { + isSucceed = false; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .AddEvent(new XBaseEventModel(item)); + } + + // + // Return Item ... + return item; + } + + /// + /// add or update an Entity (add if not exists/update if exists) ... + /// + /// + /// + /// + /// + public async Task AddOrUpdateAsync( + TEntity item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isExists = await IsExistsAsync( + id: item.Id, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + + // + var isSucceed = false; + if (isExists) + { + // + try + { + // + item = await UpdateAsync( + item: item, + id: item.Id, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + isSucceed = true; + } + catch + { + isSucceed = false; + } + } + else + { + // + try + { + // + item = await AddAsync( + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + isSucceed = true; + } + catch + { + isSucceed = false; + } + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + if (isExists) + { + // + baseRepositoryEvents + .UpdateEvent(new XBaseEventModel(item)); + } + else + { + // + baseRepositoryEvents + .AddEvent(new XBaseEventModel(item)); + } + } + + // + return item; + } + + /// + /// add a range of new Entities ... + /// + /// + /// + /// + /// + public async Task AddRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isValid = !items.IsNull() && items.HasChild(); + if (!isValid) + { + return; + } + + // + foreach (var item in items) + { + // + await AddAsync( + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + + // + // Notify Event ... + if ( + isValid && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .AddManyEvent(new XBaseEventModel>(null)); + } + } + #endregion + + // + #region Update ... + /// + /// Update an Entity values ... + /// + /// + /// + /// + /// + /// + public async Task UpdateAsync( + TKey id, + TEntity item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isExists = await IsExistsAsync( + id: id, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (!isExists) + { + return null; + } + + // + var isSucceed = false; + var exists = store[id]; + + // + try + { + // + exists = exists.UpdateData( + exists, + propertyBlackList: new List { nameof(exists.Id) } + ); + store[id] = exists; + + // + isSucceed = true; + } + catch + { + isSucceed = false; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .UpdateEvent(new XBaseEventModel(item)); + } + + // + return exists; + } + + /// + /// Update a range of Entities ... + /// + /// + /// + /// + /// + public async Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var result = false; + var isValid = !items.IsNull() && items.HasChild(); + if (!isValid) + { + return result; + } + + // + foreach (var item in items) + { + // + var updated = await UpdateAsync( + item: item, + id: item.Id, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + if (!updated.IsNullOrDefault() && !result) + { + result = true; + } + } + + // + return result; + } + #endregion + + // + #region Remove ... + /// + /// remove an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isExists = await IsExistsAsync( + id: id, + cancellationToken: cancellationToken + ); + if (!isExists) + { + return null; + } + + // + TEntity result = null; + if (softDelete && configuration.EnableSoftDelete) + { + // + store[id].Deleted = true; + result = store[id]; + } + else + { + // + result = store[id]; + store.TryRemove(id, out _); + } + + // + return result; + } + + /// + /// remove an Entity ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TEntity item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await RemoveAsync( + id: item.Id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// remove a range of exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isValid = !items.IsNull() && items.HasChild(); + if (!isValid) + { + return; + } + + // + foreach (var item in items) + { + // + await RemoveAsync( + item: item, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + } + #endregion + + // + #region Count ... + /// + /// count all exists Entities ... + /// + /// + /// + /// + public async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + includeBuilder: null, + predicate: predicator + ) + .CountAsync(cancellationToken); + + // + return result; + } + + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// + public async Task PagesCountAsync( + int pageSize, + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + int count = totalItems.HasValue ? totalItems.Value : await CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + int pagesCount = count / pageSize; + + // + if (count % pageSize > 0) + { + pagesCount++; + } + + // + return pagesCount; + } + #endregion + + // + #region Exists ... + /// + /// Check an Entity exists or not ... + /// + /// + /// + /// + /// + public async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + includeBuilder: null, + predicate: predicator + ) + .AnyAsync( + cancellationToken: cancellationToken, + predicate: x => GetKey(x).ToString() == id.ToString()); + + // + return result; + } + #endregion + + // + #region Retrieve ... + /// + /// retrieve whole items as queryable ... + /// + /// a flag for Tracking behaviour + /// an Expression for Filter Items ... + /// an Order Builder expression for Ordering Query ... + /// an Include Builder expression for Including Navigation Properties ... + /// + public IQueryable AsQueryable( + bool asNoTracking = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + var result = store.Values.AsQueryable(); + + // + // Predicate ... + if (!predicate.IsNull()) + { + result = result.Where(predicate); + } + + // + // Apply Orders ... + if (!orderBuilder.IsNull()) + { + result = orderBuilder(result); + } + + // + // Apply Includes ... + if (!includeBuilder.IsNull()) + { + result = includeBuilder(result); + } + + // + if (asNoTracking) + { + result = result.AsNoTracking(); + } + + // + return result; + } + + /// + /// retrieve an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(x => GetKey(x).ToString() == id.ToString()) + .FirstOrDefaultAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities + /// as Async Enumerable ... + /// + /// + /// + /// + /// + public IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .AsAsyncEnumerable(); + + // + return result; + } + + /// + /// find an Entity by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(predicate) + .FirstOrDefaultAsync(cancellationToken); + + // + return result; + } + + /// + /// find a collection of Entities by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(predicate) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve Entities based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Normalize Query ... + query = query.NormalizeQuery(configuration); + + // + var items = await GetAllAsync(); + 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; + } + #endregion + + // + #region Unit Of Work ... + /// + /// Save all unsaved Transactions on DbContext ... + /// used fo Unit Of Works Design Pattern ... + /// + /// + /// + public async Task SaveChangesAsync( + CancellationToken cancellationToken = default + ) + { + // + return await Task.Run( + () => 0, + cancellationToken + ); + } + #endregion + + // + #region Key ... + /// + /// Retrieve Key of Entity ... + /// + /// + /// + public TKey GetKey(TEntity item) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + + // + var keyString = string.Empty; + if (keyProp.IsNull()) + { + keyString = string.Empty; + } + else + { + keyString = keyProp.GetValue(item).ToString(); + } + + // + if (keyString.IsNullOrEmpty()) + { + return default(TKey); + } + + // + // Prevent Deserializing issues throug JsonReader ... + if (keyString.IsGuid() && typeof(TKey) == typeof(Guid)) + { + return item.Id; + } + + // + return keyString.FromJSON(); + } + + /// + /// Set Key of Entity ... + /// + /// + /// + public void SetKey( + ref TEntity item, + TKey id + ) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + if (keyProp.IsNull()) + { + return; + } + + // + Type t = Nullable.GetUnderlyingType(keyProp.PropertyType) ?? keyProp.PropertyType; + object safeValue = (id == null) ? null : Convert.ChangeType(id, t); + keyProp.SetValue(item, safeValue, null); + } + + /// + /// Handle Checking Key ... + /// + /// + /// + /// + public async Task HandleKeyAsync( + TEntity item, + CancellationToken cancellationToken = default + ) + { + // + var keyType = typeof(TKey); + + // + // Handle Guid Key Type ... + // Since EFCore has AutoIncrement on int Ids, there is no need to handle int Key types ... + if ( + ( + keyType == typeof(Guid) || + keyType == typeof(string) + ) && + keyGenerator.IsEmpty(item.Id) + ) + { + // + var newKey = await keyGenerator.GenerateKey(this); + + // + SetKey(ref item, newKey); + } + + // + return item; + } + #endregion + + // + #region Detach ... + /// + /// Detach an Entity ... + /// + /// + public void Detach(TEntity item) { } + + /// + /// Detach an Enumerable of Entities ... + /// + /// + public void Detach(IEnumerable items) { } + + /// + /// Detach a Query Result of Entity ... + /// + /// + public void Detach(XQueryResult query) { } + #endregion + #endregion + + // + #region Others ... + public void Dispose() + { } + #endregion + + // + #region Private ... + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XBaseMongoRepository.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Extensions; +using xDataService.Interfaces; +using xDataService.Models; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Providers +{ + /// + /// a Base Repository Pattern Implementation Specially for Mongo DBs using + /// EF Core Capabilities ... + /// + /// + /// + public abstract class XBaseMongoRepository : IXBaseRepository + where T : XBaseEntity + { + // + #region Properties ... + private readonly string collectionName; + public List> bulkCollection; + public readonly IMongoCollection collection; + private readonly IXKeyGenerator keyGenerator; + public readonly XDataBaseConfiguration dbConfiguration; + public readonly XDataServiceConfiguration configuration; + private readonly IXBaseRepositoryEvents baseRepositoryEvents; + #endregion + + // + #region Constructor ... + protected XBaseMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXKeyGenerator keyGenerator = null, + IXBaseRepositoryEvents baseRepositoryEvents = null + ) + { + // + this.keyGenerator = keyGenerator; + this.configuration = configuration; + this.collectionName = collectionName + .IsNullOrEmpty() ? + typeof(T).Name : + collectionName; + this.dbConfiguration = dbConfiguration; + this.baseRepositoryEvents = baseRepositoryEvents; + + // + var client = new MongoClient(dbConfiguration.GetMongoDbURI()); + var database = client.GetDatabase(dbConfiguration.GetMongoDbDatabase()); + + // + bulkCollection = new List>(); + collection = database.GetCollection( + this.collectionName + ); + } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// add a new Entity ... + /// + /// + /// + /// + /// + public async Task AddAsync( + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Handle Key ... + item = await HandleKeyAsync( + item: item, + cancellationToken: cancellationToken + ); + + // + // Add Action model to Bulk Collection ... + bulkCollection.Add(new InsertOneModel(item)); + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .AddEvent(new XBaseEventModel(item)); + } + + // + // Return result base on action Succeed ... + return isSucceed ? + item : + null; + } + + /// + /// add or update an Entity (add if not exists/update if exists) ... + /// + /// + /// + /// + /// + public async Task AddOrUpdateAsync( + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isExists = await IsExistsAsync( + id: GetKey(item), + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (!isExists) + { + // + // Handle Key ... + item = await HandleKeyAsync( + item: item, + cancellationToken: cancellationToken + ); + + // + // Add Action model to Bulk Collection ... + bulkCollection + .Add(new InsertOneModel(item)); + } + else + { + // + // Filter ... + var filter = Builders.Filter.Eq(i => i.Id, item.Id); + + // + // Add Action model to Bulk Collection ... + bulkCollection + .Add(new ReplaceOneModel(filter, item) { IsUpsert = true }); + } + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + if (isExists) + { + // + baseRepositoryEvents + .UpdateEvent(new XBaseEventModel(item)); + } + else + { + // + baseRepositoryEvents + .AddEvent(new XBaseEventModel(item)); + } + } + + // + // Return result base on action Succeed ... + return isSucceed ? + item : + null; + } + + /// + /// add a range of new Entities ... + /// + /// + /// + /// + /// + public async Task AddRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isValid = !items.IsNull() && items.HasChild(); + if (!isValid) + { + return; + } + + // + foreach (var item in items) + { + // + await AddAsync( + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + + // + // Notify Event ... + if ( + isValid && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .AddManyEvent(new XBaseEventModel>(null)); + } + } + #endregion + + // + #region Update ... + /// + /// Update an Entity values ... + /// + /// + /// + /// + /// + /// + public async Task UpdateAsync( + TKey id, + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Filter ... + var filter = Builders.Filter.Eq(i => i.Id, id); + + // + // Add Action model to Bulk Collection ... + bulkCollection.Add(new ReplaceOneModel(filter, item) { IsUpsert = false }); + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .UpdateEvent(new XBaseEventModel(item)); + } + + // + // Return result base on action Succeed ... + return isSucceed ? + item : + null; + } + + /// + /// Update a range of Entities ... + /// + /// + /// + /// + /// + public async Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var result = false; + var isValid = !items.IsNull() && items.HasChild(); + if (!isValid) + { + return result; + } + + // + foreach (var item in items) + { + // + var updated = await UpdateAsync( + item: item, + id: item.Id, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + if (!updated.IsNullOrDefault() && !result) + { + result = true; + } + } + + // + return result; + } + #endregion + + // + #region Remove ... + /// + /// remove an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Retrieve Item ... + var item = await GetAsync( + id: id, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (item.IsNull()) + { + return null; + } + + // + // Filter ... + var filter = Builders.Filter.Eq(i => i.Id, id); + + // + // Check SoftDelete ... + if (softDelete && configuration.EnableSoftDelete) + { + // + // Set Soft Delete ... + item.Deleted = true; + + // + // Update ... + // Add Action model to Bulk Collection ... + bulkCollection.Add(new ReplaceOneModel(filter, item) { IsUpsert = true }); + } + else + { + // + // Delete ... + // Add Action model to Bulk Collection ... + bulkCollection.Add(new DeleteOneModel(filter)); + } + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .RemoveEvent(new XBaseEventModel(item)); + } + + // + // Return result base on action Succeed ... + return isSucceed ? + item : + null; + } + + /// + /// remove an Entity ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + T item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await RemoveAsync( + id: item.Id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// remove a range of exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // loop through items ... + foreach (var item in items) + { + // + // Filter ... + var filter = Builders.Filter.Eq(i => i.Id, item.Id); + + // + // Check Soft Delete ... + if (softDelete && configuration.EnableSoftDelete) + { + // + // Set Soft Delete ... + item.Deleted = true; + + // + // Update ... + // Add Action model to Bulk Collection ... + bulkCollection.Add(new ReplaceOneModel(filter, item) { IsUpsert = true }); + } + else + { + // + // Delete ... + // Add Action model to Bulk Collection ... + bulkCollection.Add(new DeleteOneModel(filter)); + } + } + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .RemoveManyEvent(new XBaseEventModel>(null)); + } + } + #endregion + + // + #region Count ... + /// + /// count all exists Entities ... + /// + /// + /// + /// + public async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + includeBuilder: null, + predicate: predicator + ) + .CountAsync(cancellationToken); + + // + return result; + } + + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// + public async Task PagesCountAsync( + int pageSize, + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + int count = totalItems.HasValue ? totalItems.Value : await CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + int pagesCount = count / pageSize; + + // + if (count % pageSize > 0) + { + pagesCount++; + } + + // + return pagesCount; + } + #endregion + + // + #region Retrieve ... + /// + /// retrieve whole items as queryable ... + /// + /// a flag for Tracking behaviour + /// an Expression for Filter Items ... + /// an Order Builder expression for Ordering Query ... + /// an Include Builder expression for Including Navigation Properties ... + /// + public IQueryable AsQueryable( + bool asNoTracking = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + var result = collection + .AsQueryable(); + + // + // Apply Predicate ... + if (!predicate.IsNull()) + { + result = result + .Where(predicate); + } + + // + // Apply Includes ... + if (!includeBuilder.IsNull()) + { + // + // Since Mongo Queryable doesnt Support Includes ... + // we ignore this ... + result = (IMongoQueryable)includeBuilder(result); + } + + // + // Apply Ordering ... + if (!orderBuilder.IsNull()) + { + result = (IMongoQueryable)orderBuilder(result); + } + + // + return result; + } + + /// + /// retrieve an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(x => GetKey(x).ToString() == id.ToString()) + .FirstOrDefaultAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities + /// as Async Enumerable ... + /// + /// + /// + /// + /// + public IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .AsAsyncEnumerable(); + + // + return result; + + } + + /// + /// find an Entity by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(predicate) + .FirstOrDefaultAsync(cancellationToken); + + // + return result; + } + + /// + /// find a collection of Entities by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .Where(predicate) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve Entities based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Normalize Query ... + query = query.NormalizeQuery(configuration); + + // + var items = await GetAllAsync(); + 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; + } + #endregion + + // + #region Exists ... + /// + /// Check an Entity exists or not ... + /// + /// + /// + /// + /// + public async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + includeBuilder: null, + predicate: predicator + ) + .AnyAsync( + cancellationToken: cancellationToken, + predicate: x => GetKey(x).ToString() == id.ToString()); + + // + return result; + } + #endregion + + // + #region Unit Of Work ... + /// + /// Save all unsaved Transactions on DbContext ... + /// used fo Unit Of Works Design Pattern ... + /// + /// + /// + public async Task SaveChangesAsync( + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Write all Bulk Models which stored inside bulkCollection ... + var result = await collection + .BulkWriteAsync( + options: null, + requests: bulkCollection, + cancellationToken: cancellationToken + ); + + // + // Clear Bulk Collection ... + bulkCollection.Clear(); + + // + // Return number of Modified Documents ... + var resultCount = (int)result.ModifiedCount + + (int)result.InsertedCount + + (int)result.DeletedCount; + + // + return resultCount; + } + catch (Exception ex) + { + // + // Log Thrown Exception ... + Console.WriteLine($"XMongo Repository Exception: {ex.Message} ..."); + + // + // Return less than zero value ... + return -1; + } + } + #endregion + + // + #region Key ... + /// + /// Retrieve Key of Entity ... + /// + /// + /// + public TKey GetKey(T item) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + + // + var keyString = string.Empty; + if (keyProp.IsNull()) + { + keyString = string.Empty; + } + else + { + keyString = keyProp.GetValue(item).ToString(); + } + + // + if (keyString.IsNullOrEmpty()) + { + return default(TKey); + } + + // + // Prevent Deserializing issues throug JsonReader ... + if (keyString.IsGuid() && typeof(TKey) == typeof(Guid)) + { + return item.Id; + } + + // + return keyString.FromJSON(); + } + + /// + /// Set Key of Entity ... + /// + /// + /// + public void SetKey( + ref T item, + TKey id + ) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + if (keyProp.IsNull()) + { + return; + } + + // + Type t = Nullable.GetUnderlyingType(keyProp.PropertyType) ?? keyProp.PropertyType; + object safeValue = (id == null) ? null : Convert.ChangeType(id, t); + keyProp.SetValue(item, safeValue, null); + } + + /// + /// Handle Checking Key ... + /// + /// + /// + /// + public async Task HandleKeyAsync( + T item, + CancellationToken cancellationToken = default + ) + { + // + var keyType = typeof(TKey); + + // + // Handle Guid Key Type ... + // Since EFCore has AutoIncrement on int Ids, there is no need to handle int Key types ... + if ( + ( + keyType == typeof(Guid) || + keyType == typeof(string) + ) && + keyGenerator.IsEmpty(item.Id) + ) + { + // + var newKey = await keyGenerator.GenerateKey(this); + + // + SetKey(ref item, newKey); + } + + // + return item; + } + #endregion + + // + #region Detach ... + /// + /// Detach an Entity ... + /// + /// + public void Detach(T item) { } + + /// + /// Detach an Enumerable of Entities ... + /// + /// + public void Detach(IEnumerable items) { } + + /// + /// Detach a Query Result of Entity ... + /// + /// + public void Detach(XQueryResult query) { } + #endregion + #endregion + + // + #region Others ... + public void Dispose() { } + + public string GetPropValues(T item) + { + // + var props = item.GetType().GetProperties(); + var vals = props.Select(p => p.GetValue(p.Name)); + + // + return vals.ToJSON(); + } + #endregion + + // + #region Private ... + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XBaseRepositoryEvents.cs + +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using xDataService.Interfaces; +using xDataService.Models; + +namespace xDataService.Providers { + /// + /// Some Events can be Raised on Some Actions Happens on Repositories ... + /// this is the Base Events Class ... + /// + /// + public abstract class XBaseRepositoryEvents : IXBaseRepositoryEvents { + // + private readonly ISubject> onAddSubject; + private readonly ISubject> onUpdateSubject; + private readonly ISubject> onRemoveSubject; + + // + private readonly ISubject>> onAddManySubject; + private readonly ISubject>> onUpdateManySubject; + private readonly ISubject>> onRemoveManySubject; + + // + protected XBaseRepositoryEvents () { + // + onAddSubject = new ReplaySubject> (1); + onUpdateSubject = new ReplaySubject> (1); + onRemoveSubject = new ReplaySubject> (1); + + // + onAddManySubject = new ReplaySubject>> (1); + onUpdateManySubject = new ReplaySubject>> (1); + onRemoveManySubject = new ReplaySubject>> (1); + } + + // + public void AddEvent (XBaseEventModel model) => onAddSubject.OnNext (model); + + public void UpdateEvent (XBaseEventModel model) => onUpdateSubject.OnNext (model); + + public void RemoveEvent (XBaseEventModel model) => onRemoveSubject.OnNext (model); + + // + public void AddManyEvent (XBaseEventModel> model) => onAddManySubject.OnNext (model); + public void UpdateManyEvent (XBaseEventModel> model) => onUpdateManySubject.OnNext (model); + public void RemoveManyEvent (XBaseEventModel> model) => onRemoveManySubject.OnNext (model); + + // + public IObservable> OnAddObservable => onAddSubject.AsObservable (); + + public IObservable> OnUpdateObservable => onUpdateSubject.AsObservable (); + + public IObservable> OnRemoveObservable => onRemoveSubject.AsObservable (); + + // + public IObservable>> OnAddManyObservable => onAddManySubject.AsObservable (); + public IObservable>> OnUpdateManyObservable => onUpdateManySubject.AsObservable (); + public IObservable>> OnRemoveManyObservable => onRemoveManySubject.AsObservable (); + + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XBaseRepositoryService.cs + +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 AutoMapper; +using Microsoft.EntityFrameworkCore.Query; +using xCommons.Extensions; +using xDataService.Interfaces; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Providers +{ + /// + /// a Base Repository Service Implementation which Use a Repository Patterns Implementatin + /// and provides Data Manipulation Methods using Dtos instead of Entities ... + /// + /// it can be use of following Repository Patterns Implementation: + /// > + /// > + /// > + /// + /// + /// + /// + public abstract class XBaseRepositoryService : IXBaseRepositoryService + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + #region Properties ... + /// + /// Mapper Instance ... + /// + public IMapper Mapper { get; } + + /// + /// Repository Implementation for Data Manipulations ... + /// + public IXBaseRepository Repository { get; } + + /// + /// Mapping Profile Configuration ... + /// + /// this Configuration used for Mapping TEntity to Dto and reverse ... + /// default mapping prepared on Constructing time, other custom maps + /// must handled manually if required ... + /// + public MapperConfiguration MapperConfiguration { get; } + #endregion + + // + #region Constructor ... + public XBaseRepositoryService( + IXBaseRepository repository, + IEnumerable mapperProfiles = null + ) + { + // + Repository = repository; + + // + // Preparing Mapping Configurations ... + MapperConfiguration = new MapperConfiguration(cfg => + { + // + // Add Default Profiles ... + + // + // Entity To Dto ... + cfg.CreateMap() + .ForMember(dest => dest.Deleted, opt => opt.Ignore()); + cfg.CreateMap, XQueryResult>(); + + // + // Dto to Entity ... + cfg.CreateMap() + .ForMember(dest => dest.Deleted, opt => opt.Ignore()); + cfg.CreateMap, XQueryResult>(); + + // + // Add Provided Profiles if Provided ... + // if Same Profiles Provided, here resolved ... + if (!mapperProfiles.IsNull() && + mapperProfiles.HasChild()) + { + cfg.AddProfiles(mapperProfiles); + } + }); + + // + // Make Mapper Instance using prepared Configuration ... + Mapper = MapperConfiguration.CreateMapper(); + } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// add a new Dto ... + /// + /// + /// + /// + /// + public async Task AddAsync( + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + // Validate ... + bool isValid = !item.IsNull(); + if (!isValid) + { + return result; + } + + // + try + { + // + var entity = Mapper.Map(item); + + // + entity = await Repository.AddAsync( + item: entity, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// add or update a Dto (add if not exists/update if exists) ... + /// + /// + /// + /// + /// + public async Task AddOrUpdateAsync( + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + // Validate ... + bool isValid = !item.IsNull(); + if (!isValid) + { + return result; + } + + // + try + { + // + var entity = Mapper.Map(item); + + // + entity = await Repository.AddOrUpdateAsync( + item: entity, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// add a range of new Dtos ... + /// + /// + /// + /// + /// + public async Task AddRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + // Validate ... + bool isValid = + !items.IsNull() && + items.HasChild(); + if (!isValid) + { + return; + } + + // + try + { + // + var entities = Mapper.Map>(items); + + // + await Repository.AddRangeAsync( + items: entities, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + catch + { } + } + #endregion + + // + #region Update ... + /// + /// Update an Dto values ... + /// + /// + /// + /// + /// + /// + public async Task UpdateAsync( + TKey id, + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + try + { + // + var entity = Mapper.Map(item); + + // + entity = await Repository.UpdateAsync( + id: item.Id, + item: entity, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// Update a range of Dtos ... + /// + /// + /// + /// + /// + public async Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = + !items.IsNull() && + items.HasChild(); + if (!result) + { + return result; + } + + // + try + { + // + var entitis = Mapper.Map>(items); + + // + result = await Repository.UpdateRangeAsync( + items: entitis, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + catch + { + result = false; + } + + // + return result; + } + #endregion + + // + #region Remove ... + /// + /// remove an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + try + { + // + var entity = await Repository.RemoveAsync( + id: id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// remove an Dto ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TDto item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + try + { + // + var entity = Mapper.Map(item); + + // + entity = await Repository.RemoveAsync( + item: entity, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// remove a range of exists Dtos ... + /// + /// + /// + /// + /// + /// + public async Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !items.IsNull() && + items.HasChild(); + if (!isValid) + { + return; + } + + // + var entities = Mapper.Map>(items); + + // + try + { + // + await Repository.RemoveRangeAsync( + items: entities, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + catch + { } + } + #endregion + + // + #region Count ... + /// + /// count all exists Dtos ... + /// + /// + /// + /// + public async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// + public async Task PagesCountAsync( + int pageSize, + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.PagesCountAsync( + pageSize: pageSize, + totalItems: totalItems, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + #endregion + + // + #region Exists ... + /// + /// Check a Dto exists or not ... + /// + /// + /// + /// + /// + public async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + #endregion + + // + #region Retrieve ... + /// + /// retrieve an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + try + { + // + var entity = await Repository.GetAsync( + id: id, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// retrieve all exists Dtos ... + /// + /// + /// + /// + /// + /// + public async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + IEnumerable result = Enumerable.Empty(); + + // + try + { + // + var entities = await Repository.GetAllAsync( + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + result = Mapper.Map>(entities); + } + catch + { } + + // + return result; + } + + /// + /// retrieve all exists Dtos + /// as Async Enumerable ... + /// + /// + /// + /// + /// + /// + public async IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default + ) + { + // + var entityEnumerable = Repository.GetAllAsAsyncEnumerable( + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + await foreach (var entity in entityEnumerable) + { + // + if (cancellationToken.IsCancellationRequested) + { + yield break; + } + + // + var dto = Mapper.Map(entity); + yield return dto; + } + } + + /// + /// find an Dto by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + try + { + // + var entity = await Repository.FindOneAsync( + predicate: predicate, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// find a collection of Dtos by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + IEnumerable result = Enumerable.Empty(); + + // + try + { + // + var entities = await Repository.FindManyAsync( + predicate: predicate, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + result = Mapper.Map>(entities); + } + catch + { } + + // + return result; + } + + /// + /// retrieve Dtos based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + XQueryResult result = null; + + // + try + { + // + var entityResult = await Repository.QueryAsync( + query: query, + predicate: predicate, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + result = Mapper.Map>(entityResult); + } + catch + { } + + // + return result; + } + #endregion + + public virtual void Dispose() + { } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XEntityRegisterarHandlerService.cs + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using xCommons.Extensions; +using xDataService.Interfaces; + +namespace xDataService.Providers +{ + public class XEntityRegisterarHandlerService + { + /// + /// a Collection for Extract Registerars Services ... + /// + private readonly List registerars = []; + + /// + /// a Collection for Holding all Entity Types ... + /// + public IReadOnlyList AllEntityTypes { get; private set; } + + /// + /// Retrieve Specified Registerars Entity Types ... + /// + /// + /// + public IReadOnlyList GetEntityTypes(string[] names = null) + { + // + var items = new List(); + + // + if (!names.IsNull() && names.HasChild()) + { + // + items = [.. registerars + .Where(r => names.Contains(r.Name)) + .SelectMany(r => r.GetEntityTypes())]; + } + + // + return items.AsReadOnly(); + } + + /// + /// Load all Dynamically Registered Entities ... + /// + public void HandleRegisteredEntities() + { + // + var entityTypes = new List(); + // path = Path.Combine (Directory.GetCurrentDirectory ()); + var path = Path.Combine (AppContext.BaseDirectory); + var files = Directory.GetFiles(path, "*.dll"); + foreach (var dll in files) + { + // + // Retrieve DLL Assembly ... + var assembly = Assembly.LoadFrom(dll); + + // + // Extract Registerar Types ... + var registerarTypes = assembly + .GetExportedTypes() + .Where(t => + !t.IsAbstract && + typeof(IXEntityRegisterar).IsAssignableFrom(t)); + foreach (var registerarType in registerarTypes) + { + // + // Create Instance of Registerar Type ... + var registerar = (IXEntityRegisterar)Activator.CreateInstance(registerarType); + if (!registerar.IsNull()) + { + // + // Add Registerar Instance to Collection ... + registerars.Add(registerar); + + // + // Add Registerar Provided Entities to Collection ... + entityTypes.AddRange(registerar.GetEntityTypes()); + } + } + + // + // Converts to Readonly List ... + AllEntityTypes = entityTypes.AsReadOnly(); + } + } + + /// + /// Configure all Dynamically Registered Entities ... + /// + /// + /// + public void HandleApplyRegisteredEntitiesConfigurations( + ModelBuilder modelBuilder, + string[] names = null + ) + { + // + var selectedRegisterars = + !names.IsNull() && + names.HasChild() + ? registerars + .Where(r => names.Contains(r.Name)) + : registerars; + + // + // Loop Through Extracted Registerars ... + foreach(var registerar in selectedRegisterars) + { + // + // Configure Specified Registerars Entities ... + registerar.ConfigureEntities(modelBuilder); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XGuidKeyGenerator.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xDataService.Interfaces; +using xModels.Base; + +namespace xDataService.Providers +{ + /// + /// Default Guid Key Generator ... + /// + /// + public class XGuidKeyGenerator : XKeyGenerator, IXKeyGenerator + where TEntity : XBaseEntity + { + private readonly IXSequentialGuid sequentialGuid; + + public XGuidKeyGenerator( + IXSequentialGuid sequentialGuid = null + ) + { + this.sequentialGuid = sequentialGuid; + } + + public override async Task GenerateKey( + IXBaseRepository repository, + CancellationToken cancellationToken = default + ) + { + // + return await Task.Run(() => + { + // + // Check if provided SequentialGuid ... + if (sequentialGuid.IsNull()) + { + return Guid.NewGuid(); + } + else + { + return sequentialGuid.Next(); + } + }, cancellationToken: cancellationToken); + } + + public override bool IsEmpty(Guid id) + { + // + var result = id.IsNull() || id.IsDefaultGuid(); + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XIntKeyGenerator.cs + +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xDataService.Extensions; +using xDataService.Interfaces; +using xModels.Base; + +namespace xDataService.Providers +{ + /// + /// Default Int Key Generator ... + /// + /// + public class XIntKeyGenerator : XKeyGenerator, IXKeyGenerator + where TEntity : XBaseEntity + { + public override async Task GenerateKey( + IXBaseRepository repository, + CancellationToken cancellationToken = default + ) + { + // + var items = (await repository.GetAllAsync()) + .OrderByDescending(nameof(XBaseEntity.Id)); + var last = items + .FirstOrDefault(); + + // + var result = last.IsNull() ? 1 : last.Id + 1; + + // + return result; + } + + public override bool IsEmpty(int id) + { + // + var result = id <= 0; + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XKeyGenerator.cs + +using System.Threading; +using System.Threading.Tasks; +using xDataService.Interfaces; +using xModels.Base; + +namespace xDataService.Providers +{ + public abstract class XKeyGenerator : IXKeyGenerator + where TEntity : XBaseEntity + { + public abstract Task GenerateKey( + IXBaseRepository repository, + CancellationToken cancellationToken = default + ); + + public abstract bool IsEmpty(TKey id); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XLongKeyGenerator.cs + +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xDataService.Extensions; +using xDataService.Interfaces; +using xModels.Base; + +namespace xDataService.Providers +{ + /// + /// Default Long Key Generator ... + /// + /// + public class XLongKeyGenerator : XKeyGenerator, IXKeyGenerator + where TEntity : XBaseEntity + { + public override async Task GenerateKey( + IXBaseRepository repository, + CancellationToken cancellationToken = default + ) + { + // + var items = (await repository.GetAllAsync()) + .OrderByDescending(nameof(XBaseEntity.Id)); + var last = items + .FirstOrDefault(); + + // + var result = last.IsNull() ? 1 : last.Id + 1; + + // + return result; + } + + public override bool IsEmpty(long id) + { + // + var result = id <= 0; + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XSequentialGuid.cs + +using System; +using xDataService.Interfaces; + +namespace xDataService.Helpers +{ + /// + /// this service provide Sequential GUID mechanism for Entity Ids ... + /// + public class XSequentialGuid : IXSequentialGuid + { + private static int[] sqlOrderMap = null; + private static int[] SQLORDERMAP + { + get + { + if (sqlOrderMap == null) + { + sqlOrderMap = new int[16] { + 3, + 2, + 1, + 0, + 5, + 4, + 7, + 6, + 9, + 8, + 15, + 14, + 13, + 12, + 11, + 10 + }; + // 3 - the least significant byte in Guid ByteArray [for SQL Server ORDER BY clause] + // 10 - the most significant byte in Guid ByteArray [for SQL Server ORDERY BY clause] + } + return sqlOrderMap; + } + } + + private Guid currentGuid; + + public XSequentialGuid() + { + currentGuid = Guid.NewGuid(); + } + + public Guid GetCurrentGuid() + { + return currentGuid; + } + + public Guid Next() + { + byte[] bytes = currentGuid.ToByteArray(); + for (int mapIndex = 0; mapIndex < 16; mapIndex++) + { + int bytesIndex = SQLORDERMAP[mapIndex]; + bytes[bytesIndex]++; + if (bytes[bytesIndex] != 0) + { + break; // No need to increment more significant bytes + } + } + currentGuid = new Guid(bytes); + return currentGuid; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xDataService\Providers\XStringKeyGenerator.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xDataService.Interfaces; +using xModels.Base; + +namespace xDataService.Providers +{ + /// + /// Default Guid Key Generator ... + /// + /// + + public class XStringKeyGenerator : XKeyGenerator, IXKeyGenerator + where TEntity : XBaseEntity + { + private readonly IXSequentialGuid sequentialGuid; + + public XStringKeyGenerator(IXSequentialGuid sequentialGuid = null) + { + this.sequentialGuid = sequentialGuid; + } + + public override async Task GenerateKey( + IXBaseRepository repository, + CancellationToken cancellationToken = default + ) + { + // + await Task.CompletedTask; + + // + if (sequentialGuid.IsNull()) + { + return Guid.NewGuid().ToString(); + } + else + { + return sequentialGuid.Next().ToString(); + } + } + + public override bool IsEmpty(string id) + { + return id.IsNullOrEmpty(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Base\XBaseEvent.cs + +using System; + +namespace xEventService.Base { + public abstract class XBaseEvent { + public string Type { get; protected set; } + public DateTime Timestamp { get; protected set; } + + protected XBaseEvent () { + // + Type = GetType ().Name; + Timestamp = DateTime.UtcNow; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Configuration\XEventServiceConfiguration.cs + +using System.Collections.Generic; +using RabbitMQ.Client; + +namespace xEventService.Configuration { + /// + /// a configuration class for describing how to connect rabbit mq server + /// and how channels must be declared ... + /// + public class XEventServiceConfiguration { + public string Server { get; set; } + public int Port { get; set; } + public string Username { get; set; } + public string Password { get; set; } + + // + public bool AllowDynamicQueues { get; set; } = true; + public XQueueArgs DefaultQueueArgs { get; set; } = new XQueueArgs (); + public IDictionary Queues { get; set; } + + // + public bool AllowDynamicExchangess { get; set; } = true; + public XExchangeArgs DefaultExchangeArgs { get; set; } = new XExchangeArgs (); + public IDictionary Exchanges { get; set; } + + // + public static XEventServiceConfiguration GetDefaults () { + return new XEventServiceConfiguration { + Port = 5672, + Username = "guest", + Password = "guest", + Server = "localhost", + Queues = new Dictionary { { "XMainQueue", new XQueueArgs { Name = "XMainQueue" } } + } + }; + } + } + + public class XQueueArgs { + public string Name { get; set; } + public bool Durable { get; set; } = false; + public bool Exclusive { get; set; } = false; + public bool AutoDelete { get; set; } = true; + } + + public class XExchangeArgs { + public string Name { get; set; } + public string Type { get; set; } = ExchangeType.Fanout; + public bool Durable { get; set; } = false; + public bool AutoDelete { get; set; } = true; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\ConfigurationNodeNames.cs + +namespace xEventService.Constants +{ + public partial struct ConfigurationNodeNames { + public const string EVENT_SERVICE_NODE_NAME = "EventServiceConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\DI\XDIHelperExtension.cs + +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Interfaces; +using xEventService.Models; +using xEventService.Providers; + +namespace xEventService.DI { + public static partial class XDIHelperExtension { + /// + /// Extract EventService Configurations ... + /// + /// + /// + public static XEventServiceConfiguration GetXEventServiceConfigurations (this IConfiguration source) { + // + var configSection = source + .GetSection (ConfigurationNodeNames.EVENT_SERVICE_NODE_NAME); + var result = configSection.Get (); + + // + return result; + } + + /// + /// Register Event Service Configurations ... + /// + /// + /// + public static void AddXEventServiceConfigurations ( + this IServiceCollection services, + IConfiguration configuration + ) { + // + var config = configuration.GetXEventServiceConfigurations (); + services.AddXEventServiceConfigurations (config); + } + + /// + /// Register Event Service Configurations ... + /// + /// + /// + public static void AddXEventServiceConfigurations ( + this IServiceCollection services, + XEventServiceConfiguration configuration + ) { + // + services.AddSingleton (configuration); + } + + /// + /// Register Event Service ... + /// + /// + /// + public static void AddXEventService ( + this IServiceCollection services, + IConfiguration configuration + ) { + // + // Check Configuration Registered ... + var config = services.GetRegisteredService (); + if (config.IsNull ()) { + // + Console.WriteLine ($"XEventService: there is no provided XEventServiceConfiguration, using default ..."); + + // + // Register Default Configuration ... + services.AddXEventServiceConfigurations (configuration); + } + + // + // Register IXEventBus ... + var serviceBas = services.GetRegisteredService (); + if (serviceBas.IsNull ()) { + services.AddSingleton (); + } + } + + /// + /// Register an Event Handler ... + /// + /// + /// + /// + /// + /// + public static void AddXEventHandler ( + this IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Transient + ) + where TEvent : XEvent + where THandler : IXEventHandler { + // + services.Add (new ServiceDescriptor ( + serviceType: typeof (THandler), + implementationType: typeof (THandler), + lifetime: lifetime + )); + + // + services.Add (new ServiceDescriptor ( + serviceType: typeof (IXEventHandler), + implementationType: typeof (THandler), + lifetime: lifetime + )); + } + + /// + /// Subscribe to specific Event ... + /// + /// + /// + /// + /// + public static void XEventSubscribe ( + this IApplicationBuilder app, + bool toExchange = true + ) + where TEvent : XEvent + where THandler : IXEventHandler { + // + var eventName = typeof (TEvent).Name; + + // + // Retrieve EventBus from IOC ... + var eventBus = app.ApplicationServices.GetRequiredService (); + + // + // Subscribe to Event Handler ... + var isSubscribed = eventBus.Subscribe (); + Console.WriteLine ($"XEventService: Event Handler subscription result: {eventName} => {isSubscribed} ..."); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Extensions\ConfigurationExtensions.cs + +using System; +using RabbitMQ.Client; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Extensions { + public static class ConfigurationExtensions { + /// + /// Create Connection Factory Based on + /// provided Configuration ... + /// + /// + /// + public static ConnectionFactory GetConnectionFactory (this XEventServiceConfiguration source) { + // + // Validate Args ... + if (source.IsNull ()) { + XException.InvalidConfiguration.Throw (); + } + + // + // Create Connection Factory ... + var result = new ConnectionFactory { + Port = source.Port, + HostName = source.Server, + UserName = source.Username, + Password = source.Password + }; + + // + // Return Result ... + return result; + } + + /// + /// Create a Connection to Server ... + /// + /// + /// + public static IConnection Connect (this XEventServiceConfiguration source) { + // + // Open Connection ... + IConnection result = null; + try { + // + // Retrieve Connection Factory ... + var connectionFactory = source.GetConnectionFactory (); + + // + // Create Connection ... + result = connectionFactory.CreateConnection (); + } catch (Exception ex) { + // + Console.WriteLine ($"XEventService Exception: Connection Failed, {ex.Message} ..."); + + // + XException.ActionFailed.Throw (); + } + + // + return result; + } + + /// + /// Validate a Queue Name based on Configurations ... + /// + /// + /// + public static void ValidateQueue ( + this XEventServiceConfiguration source, + string queueName + ) { + // + // Validate Args ... + if (source.IsNull ()) { + XException.InvalidArgs.Throw (); + } + + // + // Validate Queues ... + if ((source.Queues.IsNull () && + !source.AllowDynamicQueues) || + (!source.Queues.IsNull () && + !source.Queues.Keys.HasChild () && + !source.AllowDynamicQueues)) { + // + Console.WriteLine ($"XEventService Exception: there isn't any configured Queue ..."); + + // + XException.InvalidArgs.Throw (); + } + + // + // Check queueName not empty ... + if (queueName.IsNullOrEmpty ()) { + // + Console.WriteLine ($"XEventService Exception: Invalid QueueName ..."); + + // + XException.InvalidArgs.Throw (); + } + + // + // Check queueName Exists ... + if (!source.Queues.IsNull () && + !source.Queues.ContainsKey (queueName) && + !source.AllowDynamicQueues) { + // + Console.WriteLine ($"XEventService Exception: QueueName {queueName} not found ..."); + + // + XException.InvalidArgs.Throw (); + } + } + + /// + /// Validate a Exchange Name based on Configurations ... + /// + /// + /// + public static void ValidateExchange ( + this XEventServiceConfiguration source, + string exchangeName + ) { + // + // Validate Args ... + if (source.IsNull ()) { + XException.InvalidArgs.Throw (); + } + + // + // Validate Exchanges ... + if ((source.Exchanges.IsNull () && + !source.AllowDynamicExchangess) || + (!source.Exchanges.IsNull () && + !source.Exchanges.Keys.HasChild () && + !source.AllowDynamicExchangess)) { + // + Console.WriteLine ($"XEventService Exception: there isn't any configured Exchanges ..."); + + // + XException.InvalidArgs.Throw (); + } + + // + // Check exchangeName not empty ... + if (exchangeName.IsNullOrEmpty ()) { + // + Console.WriteLine ($"XEventService Exception: Invalid ExchangeName ..."); + + // + XException.InvalidArgs.Throw (); + } + + // + // Check exchangeName Exists ... + if (!source.Exchanges.IsNull () && + !source.Exchanges.ContainsKey (exchangeName) && + !source.AllowDynamicExchangess) { + // + Console.WriteLine ($"XEventService Exception: ExchangeName {exchangeName} not found ..."); + + // + XException.InvalidArgs.Throw (); + } + } + + /// + /// Retrieve specific QueueArgs ... + /// + /// + /// + /// + public static XQueueArgs GetQueueArgs ( + this XEventServiceConfiguration source, + string queueName + ) { + // + // Validate Queue Name ... + source.ValidateQueue (queueName); + + // + var result = source.Queues + .IsNull () ? null : source + .Queues[queueName]; + if (result.IsNull ()) { + // + // Prepare Default Queue ... + result = source.DefaultQueueArgs; + if (result.IsNull ()) { + result = new XQueueArgs () { + Name = queueName + }; + } else { + result.Name = queueName; + } + } + + // + return result; + } + + /// + /// Retrieve specific ExchangeArgs ... + /// + /// + /// + /// + public static XExchangeArgs GetExchangeArgs ( + this XEventServiceConfiguration source, + string exchangeName + ) { + // + // Validate Exchange Name ... + source.ValidateExchange (exchangeName); + + // + var result = source.Exchanges + .IsNull () ? null : source + .Exchanges[exchangeName]; + if (result.IsNull ()) { + // + // Prepare Default Queue ... + result = source.DefaultExchangeArgs; + if (result.IsNull ()) { + result = new XExchangeArgs () { + Name = exchangeName, + Type = ExchangeType.Fanout + }; + } else { + result.Name = exchangeName; + } + } + + // + return result; + } + + /// + /// Create a Channel ... + /// + /// + /// + /// + public static XChannel CreateChannel ( + this XEventServiceConfiguration source, + string exchangeName = null + ) { + // + // Validate ExchangeName and Retrieve ExchangeArgs if provided ... + var exchangeArgs = exchangeName + .IsNullOrEmpty () ? + null : + source.GetExchangeArgs (exchangeName); + + // + // Create Connection ... + var connection = source.Connect (); + var channel = connection.CreateModel (); + + // + // Create XChannel Model ... + var result = new XChannel ( + exchangeName: exchangeName, + connection: connection + ); + + // + // Declaring Queue if Provided ... + if (!exchangeArgs.IsNull () && + !exchangeName.IsNullOrEmpty ()) { + // + // Declaring Queue ... + result.Channel.ExchangeDeclare ( + exchange: exchangeName, + type: exchangeArgs.Type, + durable: exchangeArgs.Durable, + autoDelete: exchangeArgs.AutoDelete + ); + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Extensions\XEventModelExtensions.cs + +using System; +using System.Text; +using xCommons.Extensions; + +namespace xEventService.Extensions { + public static class XEventModelExtensions { + /// + /// Convert an Object to bytes array for publishing + /// + /// + /// + /// + public static byte[] ToBody (this T source) { + // + var json = source + .ToJSON (); + + // + var result = json + .ToBytes (); + + // + return result; + } + + /// + /// Convert Recieved Bytes to Specific Type ... + /// + /// + /// + /// + public static T FromBody (this ReadOnlyMemory source) { + // + var jsonString = Encoding.UTF8 + .GetString ( + source.ToArray () + ); + var result = jsonString.FromJSON (); + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXEventBus.cs + +using xEventService.Models; + +namespace xEventService.Interfaces { + public interface IXEventBus { + bool Publish (T @event) where T : XEvent; + + bool Subscribe () + where TEvent : XEvent + where THandler : IXEventHandler; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXEventHandler.cs + +using System.Threading.Tasks; +using xEventService.Models; + +namespace xEventService.Interfaces { + public interface IXEventHandler : IXEventHandler + where TEvent : XEvent { + Task HandleAsync (TEvent @event); + } + + public interface IXEventHandler { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XChannel.cs + +using System; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Extensions; +using xExceptions.Constants; + +namespace xEventService.Models { + public class XChannel { + // + public IModel Channel { get; protected set; } + public string QueueName { get; protected set; } + public string ExchangeName { get; protected set; } + public IConnection Connection { get; protected set; } + + // + #region Constructors ... + public XChannel ( + string exchangeName = null, + IConnection connection = null, + IModel channel = null, + EventingBasicConsumer consumer = null + ) { + // + // Validate Connection ... + if (connection.IsNull ()) { + XException.InvalidArgs.Throw (); + } + Connection = connection; + + // + // Validate Channel ... + if (channel.IsNull ()) { + channel = connection.CreateModel (); + } + Channel = channel; + + // + // Check Queue Name and it's Declaration ... + if (!exchangeName.IsNullOrEmpty ()) { + // + ExchangeName = exchangeName; + } + } + + public XChannel ( + XEventServiceConfiguration configuration, + bool dispatchConsumersAsync = true, + string exchangeName = null + ) { + // + // Generate Factory ... + var factory = configuration.GetConnectionFactory (); + if (dispatchConsumersAsync) { + factory.DispatchConsumersAsync = true; + } + + // + // Create Cahnnel ... + Connection = factory.CreateConnection (); + Channel = Connection.CreateModel (); + + // + // Check Exchange Declaration if provided ... + if (!exchangeName.IsNullOrEmpty ()) { + // + ExchangeName = exchangeName; + var exchangeArgs = configuration.GetExchangeArgs (ExchangeName); + + // + // Declaring Exchange ... + Channel.ExchangeDeclare ( + exchange: exchangeName, + type: exchangeArgs.Type, + durable: exchangeArgs.Durable, + autoDelete: exchangeArgs.AutoDelete + ); + + // + // Create a Queue ... + var queueName = $"{exchangeName}[{Guid.NewGuid().ToString().GetDigits()}]"; + var queueArgs = configuration.GetQueueArgs (queueName); + QueueName = queueName; + Channel.QueueDeclare ( + queue: queueName, + durable: queueArgs.Durable, + exclusive: queueArgs.Exclusive, + autoDelete: queueArgs.AutoDelete + ); + + // + // Bind Queue to Exchange ... + Channel.QueueBind ( + queue: queueName, + exchange: exchangeName, + routingKey: "" + ); + } + } + #endregion + + // + #region Actions ... + /// + /// Close Channel ... + /// + public void CloseChannel () { + // + // Check Channel Exists ... + if (!this.Channel.IsNull ()) { + // + // Close Channel if it's Open ... + if (this.Channel.IsOpen) { + this.Channel.Close (); + } + + // + this.Channel.Dispose (); + this.Channel = null; + } + } + + /// + /// Close Connection ... + /// + public void CloseConnection () { + // + // Check Connection Exists ... + if (!this.Connection.IsNull ()) { + // + // Close Connection if it's Open ... + if (this.Connection.IsOpen) { + this.Connection.Close (); + } + + // + this.Connection.Dispose (); + this.Connection = null; + } + } + + /// + /// Publish a Message through Channel on Declare Queue ... + /// + /// + /// + /// + public bool Publish (T message) { + // + // Validate Args ... + if (this.Connection.IsNull () || + !this.Connection.IsOpen || + this.Channel.IsNull () || + !this.Channel.IsOpen) { + // + Console.WriteLine ($"XEventService Exception: Publish Failed ..."); + + // + XException.InvalidArgs.Throw (); + } + + // + // Check Publish Method ... + // + // Check Exchange Name ... + if (this.ExchangeName.IsNullOrEmpty ()) { + // + Console.WriteLine ($"XEventService Exception: Publish Failed, Invalid Exchange Name {ExchangeName} ..."); + + // + XException.InvalidArgs.Throw (); + } + + // + try { + // + // Publish Message through Channel to Declared Queue ... + var body = message.ToBody (); + this.Channel.BasicPublish ( + exchange: ExchangeName, + routingKey: "", + basicProperties : null, + body : body + ); + + // + return true; + } catch (Exception ex) { + // + Console.WriteLine ($"XEventService Exception: Publish Failed, {ex.Message} ..."); + + // + return false; + } + } + + public void Consume (EventingBasicConsumer consumer) { + Channel.BasicConsume ( + queue: QueueName, + autoAck: true, + consumer: consumer + ); + } + + public void ConsumeAsync (AsyncEventingBasicConsumer consumer) { + Channel.BasicConsume ( + queue: QueueName, + autoAck: true, + consumer: consumer + ); + } + + /// + /// Dispose XChannel ... + /// + public void Dispose () { + // + this.CloseChannel (); + this.CloseConnection (); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XEvent.cs + +using xEventService.Base; + +namespace xEventService.Models { + public class XEvent : XBaseEvent { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XEventBus.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client.Events; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers { + public sealed class XEventBus : IXEventBus { + // + #region Properties ... + private readonly List events; + private readonly ILogger logger; + private readonly IServiceScopeFactory serviceScopeFactory; + private readonly XEventServiceConfiguration configuration; + private readonly IDictionary> handlers; + #endregion + + // + #region Constructors ... + public XEventBus ( + ILoggerFactory loggerFactory, + IServiceScopeFactory serviceScopeFactory, + XEventServiceConfiguration configuration + ) { + // + this.configuration = configuration; + this.serviceScopeFactory = serviceScopeFactory; + + // + events = new List (); + handlers = new Dictionary> (); + logger = loggerFactory.CreateLogger (); + } + #endregion + + // + #region Abstracts ... + public bool Publish (T @event) where T : XEvent { + // + // Validate Args ... + if (@event.IsNull ()) { + return false; + } + + // + // Prepare Channel ... + XChannel channel = null; + + // + // Extract event name ... + var exchangeName = @event.GetType ().Name; + + // + // Do Action ... + try { + // + // Get XChannel Model ... + channel = configuration.CreateChannel (exchangeName); + + // + // Publish Command ... + channel.Publish (message: @event); + + // + // Return Result ... + return true; + } catch (Exception ex) { + // + logger.LogError ($"XEventService Exception: Publishing Failed {exchangeName}/{ex.Message} ..."); + + // + return false; + } finally { + // + if (!channel.IsNull ()) { + channel.Dispose (); + } + } + } + + public bool Subscribe () + where TEvent : XEvent + where THandler : IXEventHandler { + // + // Extract required data ... + var eventType = typeof (TEvent); + var eventName = eventType.Name; + var handlerType = typeof (THandler); + + // + // Check event exists in list or not ... + if (!events.Contains (eventType)) { + // + // Add it if not exists ... + events.Add (eventType); + } + + // + // Check handlers Dictionary contains list or not ... + if (!handlers.ContainsKey (eventName)) { + handlers.Add (eventName, new List ()); + } + + // + // Check handlers Subscribed before or not ... + var isHandlerSubscribed = handlers[eventName] + .Any (h => h.GetType () == handlerType); + if (isHandlerSubscribed) { + // + logger.LogError ($"XEventService Exception: Duplicate Event Handler Subscription {eventName}/{handlerType.Name} ..."); + + // + return false; + } + + // + // Register Handler ... + handlers[eventName].Add (handlerType); + + // + // Do Consuming Handler ... + ConsumeEvent (); + + // + return true; + } + #endregion + + // + #region Private ... + /// + /// Consume Specific Event ... + /// + /// + private void ConsumeEvent () + where TEvent : XEvent { + // + // Extract required data ... + var eventType = typeof (TEvent); + var eventName = eventType.Name; + + // + // Create ChannelObject ... + var channel = new XChannel ( + exchangeName: eventName, + configuration: configuration, + dispatchConsumersAsync: true + ); + + // + // Create Consumer ... + var consumer = new AsyncEventingBasicConsumer (channel.Channel); + + // + // Set Consumer Delegate ... + consumer.Received += XConsumerReceivedDelegate; + + // + // Consume Async Consumer ... + channel.ConsumeAsync (consumer); + } + + /// + /// this Delegate method calls when a message recieved ... + /// + private async Task XConsumerReceivedDelegate ( + object sender, + BasicDeliverEventArgs ea + ) { + // + // Generate Required Data ... + var eventName = ea.Exchange; + var json = Encoding.UTF8 + .GetString ( + ea.Body + .ToArray () + ); + + // + // Do Processing Event ... + try { + // + // Process Event in non Blocking Tasks ... + await ProcessEvent (eventName, json) + .ConfigureAwait (false); + } catch (Exception ex) { + // + logger.LogError ($"XEventService Exception: Processing Event failed {eventName}/{ex.Message} ..."); + + // + XException.ActionFailed.Throw (); + } finally { + // + var channel = ((EventingBasicConsumer) sender).Model; + channel.BasicAck ( + deliveryTag: ea.DeliveryTag, + multiple: false + ); + } + } + + /// + /// Here we must call registered Event Handlers to Handle the Event ... + /// + private async Task ProcessEvent ( + string eventName, + string json + ) { + // + // Check Handler Exists for current Event ... + var isExistsHandler = handlers.ContainsKey (eventName); + if (!isExistsHandler) { + return; + } + + // + // Using Service Scope Factory ... + using (var scope = serviceScopeFactory.CreateScope ()) { + // + // Get Handler Subscriptions ... + var subscriptions = handlers[eventName]; + foreach (var subscription in subscriptions) { + // + // Inject Handler from Dependency Injections ... + var handler = scope.ServiceProvider.GetService (subscription); + if (handler.IsNull ()) { + // + logger.LogInformation ($"XEventService: there is no handler Registered in DependencyInjection for {eventName}/{subscription.Name}"); + + // + continue; + } + + // + // Retrieve Event Type ... + var eventType = events.SingleOrDefault (t => t.Name == eventName); + var @event = json.FromJSON (eventType); + var conreteType = typeof (IXEventHandler<>).MakeGenericType (eventType); + + // + // Call EventHandler 'HandleAsync' method ... + await ((Task) conreteType + .GetMethod (nameof (IXEventHandler.HandleAsync)) + .Invoke (handler, new object[] { @event })) + .ConfigureAwait (true); + } + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xExceptions\Attributes\StringValueAttribute.cs + +using System; + +namespace xExceptions.Attributes +{ + /// + /// an attribute which attached to enums to carry propper string + /// + public partial class StringValueAttribute : System.Attribute { + public string StringValue { get; set; } + + public StringValueAttribute (string value) { + this.StringValue = value; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xExceptions\Constants\XException.cs + +using xExceptions.Attributes; + +namespace xExceptions.Constants +{ + public enum XException { + // + #region Invalid Exceptions 1xx ... + [StringValue ("Invalid Data ...")] + InvalidData = 100, + + // + [StringValue ("Invalid Date ...")] + InvalidDate = 101, + + // + [StringValue ("Bad Request ...")] + BadRequest = 102, + + // + [StringValue ("Invalid Args ...")] + InvalidArgs = 103, + + // + [StringValue ("Invalid Message ...")] + InvalidMessage = 104, + + // + [StringValue ("Invalid Token ...")] + InvalidToken = 105, + + // + [StringValue ("Invalid Verification Code ...")] + InvalidVerificationCode = 106, + + // + [StringValue ("Invalid Email Address ...")] + InvalidEmailAddress = 107, + + // + [StringValue ("Invalid Mobile Number ...")] + InvalidMobileNumber = 108, + + // + [StringValue ("Invalid Password ...")] + InvalidPassword = 109, + + // + [StringValue ("Inavlid Url ...")] + InavlidUrl = 110, + + // + [StringValue ("Invalid Device ...")] + InvalidDevice = 111, + + // + [StringValue ("Invalid Profile Image ...")] + InvalidProfileImage = 112, + + // + [StringValue ("Invalid File ...")] + InavlidFile = 113, + + // + [StringValue ("Invalid Username ...")] + InvalidUserName = 114, + #endregion + + // + #region Not (...) Exceptions 2xx ... + // + [StringValue ("Not Found ...")] + NotFound = 200, + + // + [StringValue ("Not Allowed ...")] + NotAllowed = 201, + + // + [StringValue ("Not Authorized ...")] + NotAuthorized = 202, + + // + [StringValue ("Not Visible ...")] + NotVisible = 203, + #endregion + + // + #region User/Registration/Account/Bann Exceptions 3xx ... + // + [StringValue ("Account Locked out Until:{0}")] + AccountLockedOut = 300, + + // + [StringValue ("Login Failed ...")] + LoginFailed = 301, + + // + [StringValue ("User Registered Before ...")] + UserRegisteredBefore = 302, + + // + [StringValue ("User Disabled ...")] + UserDisabled = 303, + // + [StringValue ("Device Banned Until:{0}")] + DeviceBanned = 304, + + // + [StringValue ("You can't Request Verification code Until:{0}")] + VerificationCodeResendNotAllowed = 305, + + // + [StringValue ("Passwords Same ...")] + PasswordsSame = 306, + + // + [StringValue ("Mobiles Same ...")] + MobilesSame = 307, + + // + [StringValue ("Emails Same ...")] + EmailsSame = 308, + + // + [StringValue ("Mobile In Used ...")] + MobileInUsed = 309, + + // + [StringValue ("Email In Used ...")] + EmailInUsed = 310, + + // + [StringValue ("Mobile Not Confirmed ...")] + MobileNotConfirmed = 311, + + // + [StringValue ("Email Not Confirmed ...")] + EmailNotConfirmed = 312, + + // + [StringValue ("Post Banned ...")] + PostBanned = 313, + + // + [StringValue ("User Banned ...")] + UserBanned = 314, + #endregion + + // + #region General Exceptions 4xx ... + // + [StringValue ("Unknown Error ...")] + Unknown = 400, + + // + [StringValue ("Duplicate ...")] + Duplicate = 401, + + // + [StringValue ("Action Failed ...")] + ActionFailed = 402, + + // + [StringValue ("Expired Date ...")] + ExpiredDate = 403, + + // + [StringValue ("Unavailable ...")] + Unavailable = 404, + + // + [StringValue ("Max Allowed Tag Exceeded ...")] + MaxAllowedTagExceeded = 405, + + // + [StringValue ("Timed out ...")] + Timeout = 406, + #endregion + + // + #region File Exceptions 5xx ... + // + [StringValue ("Empty File ...")] + EmptyFile = 500, + + // + [StringValue ("max File size exceeded ...")] + MaxFileSizeExceeded = 501, + + // + [StringValue ("Unsupported File Type ...")] + UnsupportedFileType = 502, + + // + [StringValue ("Max Allowed Media Exceeded ...")] + MaxAllowedMediaExceeded = 503, + #endregion + + // + #region Configuration Exceptions 6xx ... + // + [StringValue ("Invalid Configuration ...")] + InvalidConfiguration = 600, + + // + [StringValue ("Storage Service Initiallization Failed ...")] + StorageServiceInitialFailed = 601, + + // + [StringValue ("Message Service Initialization Failed ...")] + MessageServiceInitialFailed = 602, + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xExceptions\Models\XError.cs + +namespace xExceptions.Models { + public partial class XError { + /// + /// th ID of Error + /// + /// + public int Id { get; set; } + + /// + /// the String Value of Error + /// + /// + public string Message { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Configurations\Entities\XFileEntityConfiguration.cs + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xDataService.Providers; +using xFileService.Constants; +using xFileService.Models.Entities; + +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); + + // + // 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.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xDataService.Models; +using xExceptions.Constants; +using xFileService.Interfaces; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xFileService.Providers.Dtos; +using xIdentityService.Interfaces; +using xTagService.Controllers; + +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 Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xDataService.Controllers; +using xDataService.Interfaces; +using xFileService.Interfaces.Entities; +using xFileService.Models.Entities; + +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 Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xFileService.Interfaces.Entities; +using xFileService.Models.Entities; +using xFileService.Providers.Entities; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xPushService.Interfaces; + +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 Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xDataService.Controllers; +using xDataService.Interfaces; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; + +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.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xFileService.Providers.Dtos; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xPushService.Interfaces; + +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 Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xCommons.Helpers; +using xDataService.Constants; +using xDataService.Db; +using xDataService.DI; +using xDataService.Models; +using xExceptions.Constants; +using xPushService.DI; +using xPushService.Helpers; +using xFileService.Helpers; +using xFileService.Interfaces.Dtos; +using xFileService.Interfaces.Entities; +using xFileService.Providers.Dtos; +using xFileService.Providers.Entities; +using xFileService.Interfaces; +using xFileService.Providers; +using xFileService.Constants; + +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.Collections.Generic; +using System.Linq; +using xCommons.Extensions; +using xDataService.Extensions; +using xDataService.Models; +using xFileService.Models.Dtos; +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.Interfaces; +using xFileService.Interfaces.Entities; +using xFileService.Models.Entities; +using xFileService.Providers.Entities; +using xFileService.Providers.GraphQL; + +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\Dtos\IXFileDtoHub.cs + +using System; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xPushService.Interfaces; + +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 xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xFileService.Providers.Dtos; +using xPushService.Interfaces; + +namespace xFileService.Interfaces.Dtos +{ + public interface IXFileServiceProvider : IXBaseDtoProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Interfaces\Entities\IXFileEntityHub.cs + +using System; +using xFileService.Models.Entities; +using xPushService.Interfaces; + +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 xFileService.Models.Entities; +using xFileService.Providers.Entities; +using xPushService.Interfaces; + +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\Interfaces\IXFileProvider.cs + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xFileService.Providers.Dtos; +using xIdentityModels.Models; +using xIdentityService.Interfaces; +using xTagService.Interfaces; + +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.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xFileService.Providers.Dtos; +using xIdentityService.Interfaces; +using xTagService.Interfaces; + +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 xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xFileService.Providers.Dtos; +using xTagService.Interfaces; + +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\Models\Dtos\XFileDto.cs + +using System; +using xCommons.Constants; +using xModels.Base; +using xModels.Dtos; + +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 System.ComponentModel.DataAnnotations; +using xCommons.Constants; +using xModels.Base; + +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\Dtos\XFileDtoHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xPushService.Base; + +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 System.Collections.Generic; +using AutoMapper; +using xDataService.Providers; +using xFileService.Interfaces.Dtos; +using xFileService.Interfaces.Entities; +using xFileService.Models.Dtos; +using xFileService.Models.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 Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xIdentityService.Interfaces; +using xPushService.Base; + +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.Configuration; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Providers; +using xFileService.Interfaces.Entities; +using xFileService.Models.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 Microsoft.Extensions.Logging; +using xFileService.Interfaces.Entities; +using xFileService.Models.Entities; +using xPushService.Base; + +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.Configuration; +using xDataService.Providers; +using xFileService.Interfaces.Entities; +using xFileService.Models.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.Interfaces.Entities; +using xFileService.Models.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.Configuration; +using xDataService.Providers; +using xFileService.Interfaces.Entities; +using xFileService.Models.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.Interfaces.Entities; +using xFileService.Models.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 Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xFileService.Interfaces.Entities; +using xFileService.Models.Entities; +using xIdentityService.Interfaces; +using xPushService.Base; + +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.Configuration; +using xDataService.Providers; +using xFileService.Interfaces.Entities; +using xFileService.Models.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.Configuration; +using xDataService.GraphQL; +using xFileService.Constants; +using xFileService.Interfaces.Entities; +using xFileService.Models.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.Configuration; +using xDataService.GraphQL; +using xFileService.Interfaces.Entities; +using xFileService.Models.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); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xFileService\Providers\XFileEntityRegisterar.cs + +using System; +using Microsoft.EntityFrameworkCore; +using xDataService.Interfaces; +using xDataService.Providers; +using xFileService.Configurations.Entities; +using xFileService.Models.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.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Extensions.FileProviders; +using xCommons.Extensions; +using xDataService.Models; +using xExceptions.Constants; +using xFileService.Extensions; +using xFileService.Interfaces; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xFileService.Providers.Dtos; +using xIdentityModels.Models; +using xStorageService.Interfaces; +using xTagService.Providers; + +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: new string[] { "admin" } + ) + { + 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 xFileService.Interfaces; +using xFileService.Interfaces.Dtos; +using xFileService.Models.Dtos; +using xFileService.Models.Entities; +using xFileService.Providers.Dtos; +using xIdentityModels.Models; +using xTagService.Interfaces; +using xTagService.Providers; + +namespace xFileService.Providers +{ + public class XFileTagProvided : XBaseTagProvided, IXFileTagProvided + { + public XFileTagProvided( + IXFileTagProvider tagProvider, + IXFileServiceProvider provider + ) : base( + provider: provider, + tagProvider: tagProvider, + permittedRoles: new string[] { "admin" } + ) + { } + + 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 xFileService.Constants; +using xFileService.Interfaces; +using xTagService.Interfaces.Dtos; +using xTagService.Providers; + +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\xHttpService\Configurations\XHttpServiceConfiguration.cs + +namespace xHttpService.Configurations { + public partial class XHttpServiceConfiguration { + /// + /// Determines Http Handler Check SSL or not ... + /// + /// + public bool DisableSSLCheck { get; set; } + public int DefaultClientTimeout { get; set; } = 360; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xHttpService\Constants\ConfigurationNodeNames.cs + +namespace xHttpService.Constants { + public partial struct ConfigurationNodeNames { + public const string HTTP_SERVICE_NODE_NAME = "HttpServiceConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xHttpService\Constants\XHttpMethod.cs + +using xExceptions.Attributes; + +namespace xHttpService.Constants { + public enum XHttpMethod { + [StringValue ("GET")] + GET, + + [StringValue ("POST")] + POST, + + [StringValue ("PUT")] + PUT, + + [StringValue ("DELETE")] + DELETE + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xHttpService\DI\XDIHelperExtension.cs + +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xHttpService.Configurations; +using xHttpService.Constants; +using xHttpService.Interfaces; +using xHttpService.Providers; + +namespace xHttpService.DI { + public static partial class XDIHelperExtension { + /// + /// Retrieve XHttpService Configuration from AppSettings + /// + /// + /// + public static XHttpServiceConfiguration GetXHttpServiceConfiguration (this IConfiguration configuration) { + // + var xHttpSection = configuration.GetSection (ConfigurationNodeNames.HTTP_SERVICE_NODE_NAME); + return xHttpSection.Get (); + } + + /// + /// Register XHttpService Configuration + /// + /// + /// + public static void AddXHttpServiceConfiguration ( + this IServiceCollection services, + IConfiguration configuration + ) { + // + var httpServiceConfiguration = configuration.GetXHttpServiceConfiguration (); + services.AddXHttpServiceConfiguration(httpServiceConfiguration); + } + + /// + /// Register XHttpService Configuration + /// + /// + /// + public static void AddXHttpServiceConfiguration ( + this IServiceCollection services, + XHttpServiceConfiguration configuration + ) { + // + if (configuration.IsNull ()) { + Console.WriteLine($"xHttpService: there is no provided configurations, using default ..."); + configuration = new XHttpServiceConfiguration (); + } + + // + services.AddSingleton (configuration); + } + + /// + /// Register XHttpService on DI ... + /// + /// + /// + public static void AddXHttpService ( + this IServiceCollection services, + IConfiguration configuration + ) { + // + // Register Service Configuration ... + if (services.GetRegisteredService ().IsNull ()) { + services.AddXHttpServiceConfiguration (configuration); + } + + // + // Register Service ... + services.AddSingleton (); + } + + /// + /// Register XHttpService on DI ... + /// + /// + /// + public static void AddXHttpService ( + this IServiceCollection services, + XHttpServiceConfiguration configuration + ) { + // + // Register Service Configuration ... + if (services.GetRegisteredService ().IsNull ()) { + services.AddXHttpServiceConfiguration (configuration); + } + + // + // Register Service ... + services.AddSingleton (); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xHttpService\Extensions\RestSharpExtensions.cs + +using System.ComponentModel.DataAnnotations; +using AutoMapper.Mappers; +using RestSharp; +using xCommons.Constants; +using xCommons.Extensions; +using xModels.Dtos; + +namespace xHttpService.Extensions +{ + public static partial class RestSharpExtensions + { + /// + /// Attach an instance of XQuery to a RestRequest object + /// + /// + /// + /// + public static RestRequest AttachXQuery( + this RestRequest source, + XQuery query + ) + { + // + if (query.IsNull()) + { + return source; + } + + // + // ContainsDetail ... + // + var containsDetailKey = System.Uri.EscapeDataString(nameof(query.ContainsDetail)); + var containsDetailValue = query.ContainsDetail.AsHttpParamString(); + source.AddQueryParameter(containsDetailKey, containsDetailValue); + + // + // IsAscending ... + var isAscendingKey = System.Uri.EscapeDataString(nameof(query.IsAscending)); + var isAscendingValue = query.IsAscending.AsHttpParamString(); + source.AddQueryParameter(isAscendingKey, isAscendingValue); + + // + // Filter ... + if (!query.Filter.IsNullOrEmpty()) + { + // + var key = System.Uri.EscapeDataString(nameof(query.Filter)); + var value = query.Filter.AsHttpParamString(); + source.AddQueryParameter(key, value); + } + + // + // SortBy ... + if (!query.SortBy.IsNullOrEmpty()) + { + // + var key = System.Uri.EscapeDataString(nameof(query.SortBy)); + var value = query.SortBy.AsHttpParamString(); + source.AddQueryParameter(key, value); + } + + // + // Page ... + if (query.Page > 0) + { + // + var key = System.Uri.EscapeDataString(nameof(query.Page)); + var value = query.Page.AsHttpParamString(); + source.AddQueryParameter(key, value); + } + + // + // PageSize ... + if (query.PageSize > 0) + { + // + var key = System.Uri.EscapeDataString(nameof(query.PageSize)); + var value = query.PageSize.AsHttpParamString(); + source.AddQueryParameter(key, value); + } + + // + return source; + } + + /// + /// Add Dest User to Query Strings ... + /// + /// + /// + /// + public static RestRequest AttachDestUser( + this RestRequest source, + string destUser + ) + { + // + if (destUser.IsNullOrEmpty()) + { + return source; + } + + // + var destUserKey = System.Uri.EscapeDataString(nameof(destUser)); + var destUserValue = destUser.AsHttpParamString(); + source.AddQueryParameter(destUserKey, destUserValue); + + // + return source; + } + + /// + /// Add XPoweredBy Header to specific RestRequest Headers ... + /// + /// + /// + /// + public static RestRequest AddXPoweredBy( + this RestRequest source, + string poweredByValue + ) + { + // + source.AddHeader( + XAuthorization.XPoweredBy, + poweredByValue + ); + + // + return source; + } + + /// + /// Add XAccessToken Header to specific RestRequest Headers ... + /// + /// + /// + /// + public static RestRequest AddXAccessToken( + this RestRequest source, + string token + ) + { + // + token = $"{XAuthentication.BEARER} {token}"; + + // + source.AddHeader( + XAuthorization.Header, + token + ); + + // + return source; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xHttpService\Interfaces\IXHttpProvider.cs + +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using RestSharp; +using xHttpService.Constants; +using xIdentityModels.Models; + +namespace xHttpService.Interfaces { + public partial interface IXHttpProvider { + HttpClient GetHttpClient (); + RestClient GetRestClient ( + string baseUrl = null, + string poweredByValue = null, + XTokenResponse tokens = null + ); + RestRequest GetRestRequet ( + string endpoint, + IDictionary @params = null, + IDictionary headers = null, + IDictionary queryStrings = null + ); + Task RunRestRequest ( + RestClient client, + RestRequest request, + XHttpMethod method + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xHttpService\Providers\XHttpProvider.cs + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RestSharp; +using RestSharp.Authenticators; +using xCommons.Constants; +using xCommons.Extensions; +using xExceptions.Constants; +using xHttpService.Configurations; +using xHttpService.Constants; +using xHttpService.Interfaces; +using xIdentityModels.Models; +using xModels.Base; + +namespace xHttpService.Providers { + public partial class XHttpProvider : XBaseClass, IXHttpProvider { + + private readonly XHttpServiceConfiguration configuration; + + public XHttpProvider ( + ILoggerFactory loggerFactory, + XHttpServiceConfiguration configuration + ) : base (loggerFactory) { + this.configuration = configuration; + } + + // + #region Actions ... + /// + /// Get an Instance of Http Client + /// + /// + public HttpClient GetHttpClient () { + // + HttpClient httpClient = null; + httpClient = new HttpClient (); + + // + if (configuration.DisableSSLCheck) { + // + var httpHandler = new HttpClientHandler (); + httpHandler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; + + // + httpClient = new HttpClient (httpHandler); + } + + // + // Set Timeout ... + httpClient.Timeout = TimeSpan + .FromSeconds (configuration.DefaultClientTimeout); + + // + return httpClient; + } + + /// + /// Get an Instance of RestClient + /// + /// + /// + /// + /// + public RestClient GetRestClient ( + string baseUrl = null, + string poweredByValue = null, + XTokenResponse tokens = null + ) { + // + var client = new RestClient (baseUrl); + + // + // Add XPowered By to Default Headers ... + if (!poweredByValue.IsNullOrEmpty ()) { + client.AddDefaultHeader (XAuthorization.XPoweredBy, poweredByValue); + } + + // + // Set Authenticator ... + if (!tokens.IsNull () && + !tokens.AccessToken.IsNullOrEmpty ()) { + client.Authenticator = new JwtAuthenticator (tokens.AccessToken); + } + + // + // Refresh Token ... + if (!tokens.IsNull () && + !tokens.RefreshToken.IsNullOrEmpty ()) { + client.AddDefaultHeader (XAuthorization.RefreshToken, tokens.RefreshToken); + } + + // + // ExpiresAt ... + if (!tokens.IsNull () && + tokens.ExpiresAt > 0) { + client.AddDefaultHeader (XAuthorization.ExpiresAt, tokens.ExpiresAt.AsHttpParamString ()); + } + + // + // Set Default Timeout ... + client.Timeout = configuration.DefaultClientTimeout; + + // + // SSL Validation Handler ... + if (configuration.DisableSSLCheck) { + client.RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true; + } + + // + return client; + } + + /// + /// Get an Instance of RestRequest + /// + /// + /// + /// + /// + /// + public RestRequest GetRestRequet ( + string endpoint, + IDictionary @params = null, + IDictionary headers = null, + IDictionary queryStrings = null + ) { + // + var url = endpoint.ToString (); + + // + // Add Route Payloads ... + if (!@params.IsNull () && @params.Count > 0) { + // + var enumerator = @params.GetEnumerator (); + while (enumerator.MoveNext ()) { + // + var item = enumerator.Current; + + // + url = url.Replace (item.Key, item.Value.ToUrlEncoded ()); + } + } + + // + // Add Query Strings ... + if (!queryStrings.IsNull () && queryStrings.Count > 0) { + // + url = $"{url}?"; + var enumerator = queryStrings.GetEnumerator (); + while (enumerator.MoveNext ()) { + // + var item = enumerator.Current; + + // + url += $"{item.Key}={item.Value.ToUrlEncoded ()}&"; + } + + // + // remove lates & ... + if (url.EndsWith ("&")) { + url = url.Substring (0, url.Length - 1); + } + } + + // + // Create request ... + var request = new RestRequest (url, DataFormat.Json); + + // + // Add Headers to request ... + if (!headers.IsNull () && headers.Count > 0) { + // + var enumerator = headers.GetEnumerator (); + while (enumerator.MoveNext ()) { + // + var item = enumerator.Current; + request.AddHeader (item.Key, item.Value); + } + } + + // + return request; + } + + /// + /// Handle Call Specific Request and retrieve Response + /// + /// + /// + /// + /// + /// + public async Task RunRestRequest ( + RestClient client, + RestRequest request, + XHttpMethod method + ) { + // + var response = await Task.Run (() => { + // + IRestResponse resp = null; + + // + // Call Endpoint Service using Specified Method ... + switch (method) { + // + // Get ... + case XHttpMethod.GET: + resp = client.Get (request); + break; + + // + // Put ... + case XHttpMethod.PUT: + resp = client.Put (request); + break; + + // + // Post ... + case XHttpMethod.POST: + resp = client.Post (request); + break; + + // + // Delete ... + case XHttpMethod.DELETE: + resp = client.Delete (request); + break; + } + + // + if (!resp.IsSuccessful) { + // + // UnAuthorized ... + if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) { + XException.NotAuthorized.Throw (); + } + + // + // NotFound ... + if (resp.StatusCode == System.Net.HttpStatusCode.NotFound) { + XException.NotFound.Throw (); + } + + // + // Timeout ... + if (!resp.ErrorMessage.IsNullOrEmpty () && + resp.ErrorMessage.Contains ("timed out")) { + XException.Timeout.Throw (); + } + + // + // ErrorMessage ... + if (!resp.ErrorMessage.IsNullOrEmpty ()) { + throw new Exception (resp.ErrorMessage); + } + + // + // Default Exception ... + XException.BadRequest.Throw (); + } + + // + var result = resp.Content.FromJSON (); + return result; + }); + + // + return response; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityHelper\Extensions\XIdentityHelperExtensions.cs + +using System; +using System.Linq; +using xCommons.Extensions; +using xIdentityModels.Models; + +namespace xIdentityHelper.Extensions +{ + public static class XIdentityHelperExtensions + { + /// + /// Check User Claims ia Admin Role or not ... + /// + /// + /// + public static bool IsAdmin(this XUserClaimsInfoDto source) + { + // + var result = false; + + // + if (source.IsNull() || + source.Roles.IsNull() || + !source.Roles.HasChild()) + { + return result; + } + + // + result = source.Roles.Contains(XUserRoles.ADMIN); + + // + return result; + } + + /// + /// Check Actor is Valid for UserSelectByParam or not ... + /// + /// + /// + /// + public static bool IsValidActor(this XUserClaimsInfoDto source, string userSelectByParam) + { + // + var result = false; + + // + if (source.IsNull() || + userSelectByParam.IsNullOrEmpty()) + { + return result; + } + + // + bool isAdmin = source.IsAdmin(); + result = isAdmin || + userSelectByParam == source.Email || + userSelectByParam == source.UserId || + userSelectByParam == source.UserName || + userSelectByParam == source.PhoneNumber; + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityHelper\Extensions\XUserRoleExtensions.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using xCommons.Extensions; +using xExceptions.Constants; + +namespace xIdentityHelper.Extensions +{ + public static class XUserRoleExtensions + { + /// + /// Converts an String to XUserRole ... + /// + /// + /// + public static XUserRole ToXUserRole(this string role) + { + // + var result = XUserRole.User; + + // + // Validate ... + if (role.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + var roles = new List + { + role + }; + result = roles.GetTopRole(); + + // + return result; + } + + /// + /// Retrieve User Top Role based on Role Lists ... + /// + /// + /// + public static XUserRole GetTopRole(this IEnumerable roles) + { + // + // Validate ... + if (roles.IsNull() || !roles.HasChild()) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize Roles ... + roles = roles.Select(r => r.ToNormalString()); + + // + var result = XUserRole.User; + + // + // Admin ... + if (roles.Contains(XUserRole.Admin.GetStringValue().ToNormalString())) + { + result = XUserRole.Admin; + } + // + // ResponsibleManager ... + else if (roles.Contains(XUserRole.ResponsibleManager.GetStringValue().ToNormalString())) + { + result = XUserRole.ResponsibleManager; + } + // + // Journalist ... + else if (roles.Contains(XUserRole.Journalist.GetStringValue().ToNormalString())) + { + result = XUserRole.Journalist; + } + // + // ChiefClerk ... + else if (roles.Contains(XUserRole.ChiefClerk.GetStringValue().ToNormalString())) + { + result = XUserRole.ChiefClerk; + } + // + // ChiefClerkAssistant ... + else if (roles.Contains(XUserRole.ChiefClerkAssistant.GetStringValue().ToNormalString())) + { + result = XUserRole.ChiefClerkAssistant; + } + // + // Actuary ... + else if (roles.Contains(XUserRole.Actuary.GetStringValue().ToNormalString())) + { + result = XUserRole.Actuary; + } + // + // ActuaryAssistant ... + else if (roles.Contains(XUserRole.ActuaryAssistant.GetStringValue().ToNormalString())) + { + result = XUserRole.ActuaryAssistant; + } + // + // Reporter ... + else if (roles.Contains(XUserRole.Reporter.GetStringValue().ToNormalString())) + { + result = XUserRole.Reporter; + } + // + // ReporterAssistant ... + else if (roles.Contains(XUserRole.ReporterAssistant.GetStringValue().ToNormalString())) + { + result = XUserRole.ReporterAssistant; + } + // + // Agent ... + else if (roles.Contains(XUserRole.Agent.GetStringValue().ToNormalString())) + { + result = XUserRole.Agent; + } + // + // User ... + else if (roles.Contains(XUserRole.User.GetStringValue().ToNormalString())) + { + result = XUserRole.User; + } + else + { + XException.InvalidData.Throw(); + } + + // + return result; + } + + /// + /// Check User Roles has Specified Permissions or not ... + /// + /// + /// + /// + public static bool HasRolePermissions(this IEnumerable roles, XUserRole role) + { + // + // Validate ... + var result = + !roles.IsNull() && + roles.HasChild(); + if (!result) + { + return result; + } + + // + try + { + // + var topRole = roles.GetTopRole(); + + // + result = (int)topRole >= (int)role; + } + catch + { + result = false; + } + + // + return result; + } + public static bool HasRolePermissions(this IEnumerable roles, string role) + { + // + // Validate ... + var result = + !roles.IsNull() && + roles.HasChild() && + !role.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + var roleEnum = role.ToXUserRole(); + result = roles.HasRolePermissions(roleEnum); + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityHelper\XApiScopeHelper.cs + +using xExceptions.Attributes; + +namespace xIdentityHelper { + public static class XApiScopeHelper { + public partial struct XApiScopes { + public const string Read = "XAPI.read"; + public const string Write = "XAPI.write"; + public const string Admin = "XAPI.admin"; + public const string Manage = "manage"; + } + + public enum XApiScope { + [StringValue ("XAPI.read")] + Read, + + [StringValue ("XAPI.write")] + Write, + + [StringValue ("XAPI.admin")] + Admin, + + [StringValue ("manage")] + Manage, + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityHelper\XAuthorizationHelper.cs + +using System.Collections.Generic; +using IdentityModel; +using Microsoft.AspNetCore.Authorization; +using xCommons.Authorization; +using xCommons.Constants; +using xCommons.Extensions; +using static xIdentityHelper.XApiScopeHelper; + +namespace xIdentityHelper +{ + public class XAuthorizationHelper + { + public static IDictionary GetXAuthorizationPolicies() + { + // + var result = new Dictionary(); + + // + RequiredRolesRequirement roleRequirements = null; + + // + #region Build Policies ... + // + // Scope Policies ... + #region Scope Policies ... + // + // Read Scope ... + result.Add(XPolicies.ReadAccess, + new AuthorizationPolicyBuilder() + .RequireClaim(JwtClaimTypes.Scope, XApiScopes.Read) + .Build() + ); + + // + // Write Scope ... + result.Add(XPolicies.WriteAccess, + new AuthorizationPolicyBuilder() + .RequireClaim( + JwtClaimTypes.Scope, + XApiScopes.Read, + XApiScopes.Write + ) + .Build() + ); + + // + // Admin Scope ... + result.Add(XPolicies.AdminAccess, + new AuthorizationPolicyBuilder() + .RequireClaim( + JwtClaimTypes.Scope, + XApiScopes.Read, + XApiScopes.Write, + XApiScopes.Admin + ) + .Build() + ); + + // + // Manage Scope ... + result.Add(XPolicies.ManageAccess, + new AuthorizationPolicyBuilder() + .RequireClaim( + JwtClaimTypes.Scope, + XApiScopes.Read, + XApiScopes.Write, + XApiScopes.Admin, + XApiScopes.Manage + ) + .Build() + ); + #endregion + + // + // Roles Policies ... + #region Roles Policies ... + // + // User ... + #region User Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.USER, + XUserRoles.AGENT, + XUserRoles.REPORTER_ASSISTANT, + XUserRoles.REPORTER, + XUserRoles.ACTUARY_ASSISTANT, + XUserRoles.ACTUARY, + XUserRoles.CHIEF_CLERK_ASSISTANT, + XUserRoles.CHIEF_CLERK, + XUserRoles.JOURNALIST, + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.ADMIN + ); + + // + // User Policy ... + result.Add(XPolicies.User, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled User Policy ... + result.Add(XPolicies.EnabledUser, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + + // + // Agent ... + #region Agent Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.AGENT, + XUserRoles.REPORTER_ASSISTANT, + XUserRoles.REPORTER, + XUserRoles.ACTUARY_ASSISTANT, + XUserRoles.ACTUARY, + XUserRoles.CHIEF_CLERK_ASSISTANT, + XUserRoles.CHIEF_CLERK, + XUserRoles.JOURNALIST, + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.ADMIN + ); + + // + // Agent Policy ... + result.Add(XPolicies.Agent, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled Agent Policy ... + result.Add(XPolicies.EnabledAgent, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + + // + // ReporterAssistant ... + #region Reporter Assistant Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.REPORTER_ASSISTANT, + XUserRoles.REPORTER, + XUserRoles.ACTUARY_ASSISTANT, + XUserRoles.ACTUARY, + XUserRoles.CHIEF_CLERK_ASSISTANT, + XUserRoles.CHIEF_CLERK, + XUserRoles.JOURNALIST, + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.ADMIN + ); + + // + // ReporterAssistant ... + result.Add(XPolicies.ReporterAssistant, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled ReporterAssistant ... + result.Add(XPolicies.EnabledReporterAssistant, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + + // + // Reporter ... + #region Reporter Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.REPORTER, + XUserRoles.ACTUARY_ASSISTANT, + XUserRoles.ACTUARY, + XUserRoles.CHIEF_CLERK_ASSISTANT, + XUserRoles.CHIEF_CLERK, + XUserRoles.JOURNALIST, + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.ADMIN + ); + + // + // Reporter ... + result.Add(XPolicies.Reporter, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled Reporter ... + result.Add(XPolicies.EnabledReporter, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + + // + // ActuaryAssistant ... + #region Actuary Assistant Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.ACTUARY_ASSISTANT, + XUserRoles.ACTUARY, + XUserRoles.CHIEF_CLERK_ASSISTANT, + XUserRoles.CHIEF_CLERK, + XUserRoles.JOURNALIST, + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.ADMIN + ); + + // + // ActuaryAssistant ... + result.Add(XPolicies.ActuaryAssistant, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled ActuaryAssistant ... + result.Add(XPolicies.EnabledActuaryAssistant, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + + // + // Actuary ... + #region Actuary Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.ACTUARY, + XUserRoles.CHIEF_CLERK_ASSISTANT, + XUserRoles.CHIEF_CLERK, + XUserRoles.JOURNALIST, + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.ADMIN + ); + + // + // Actuary ... + result.Add(XPolicies.Actuary, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled Actuary ... + result.Add(XPolicies.EnabledActuary, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + + // + // ChiefClerkAssistant ... + #region Chief Clerk Assistant Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.CHIEF_CLERK_ASSISTANT, + XUserRoles.CHIEF_CLERK, + XUserRoles.JOURNALIST, + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.ADMIN + ); + + // + // ChiefClerkAssistant ... + result.Add(XPolicies.ChiefClerkAssistant, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled ChiefClerkAssistant ... + result.Add(XPolicies.EnabledChiefClerkAssistant, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + + // + // ChiefClerk ... + #region Chief Clerk Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.CHIEF_CLERK, + XUserRoles.JOURNALIST, + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.ADMIN + ); + + // + // ChiefClerk ... + result.Add(XPolicies.ChiefClerk, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled ChiefClerk ... + result.Add(XPolicies.EnabledChiefClerk, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + + // + // Journalist ... + #region Journalist Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.JOURNALIST, + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.ADMIN + ); + + // + // Journalist ... + result.Add(XPolicies.Journalist, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled Journalist ... + result.Add(XPolicies.EnabledJournalist, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + + // + // ResponsibleManager ... + #region Responsible Manager Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.ADMIN + ); + + // + // ResponsibleManager ... + result.Add(XPolicies.ResponsibleManager, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled ResponsibleManager ... + result.Add(XPolicies.EnabledResponsibleManager, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + + // + // Admin ... + #region Admin Related Policies ... + // + roleRequirements = new RequiredRolesRequirement( + XUserRoles.ADMIN + ); + + // + // Admin Policy ... + result.Add(XPolicies.Admin, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .Build() + ); + + // + // Enabled Admin Policy ... + result.Add(XPolicies.EnabledAdmin, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(roleRequirements) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + #endregion + + // + // Complex Policies ... + #region AdminOrAgent Related Policies ... + // + // Admin Or Agent Policy ... + result.Add(XPolicies.AdminOrAgent, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(new RequiredRolesRequirement( + XUserRoles.ADMIN, + XUserRoles.AGENT + )) + .Build() + ); + + // + // Enabled Admin Or Agent Policy ... + result.Add(XPolicies.EnabledAdminOrAgent, + new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(new RequiredRolesRequirement( + XUserRoles.ADMIN, + XUserRoles.AGENT + )) + .RequireClaim( + XCustomClaims.IsBanned, + false.ToString(), + false.ToString() + .ToNormalString() + ) + .RequireClaim( + XCustomClaims.IsEnabled, + true.ToString(), + true.ToString() + .ToNormalString() + ) + .Build() + ); + #endregion + #endregion + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityHelper\XPolicies.cs + +namespace xIdentityHelper +{ + public partial struct XPolicies + { + // + // Scopes ... + public const string ReadAccess = "readScopePolicy"; + public const string WriteAccess = "writeScopePolicy"; + public const string AdminAccess = "adminScopePolicy"; + public const string ManageAccess = "manageScopePolicy"; + // + // Admin ... + public const string Admin = "adminPolicy"; + public const string EnabledAdmin = "enabledAdminPolicy"; + // + // Responsible Manager ... + public const string ResponsibleManager = "responsibleManagerPolicy"; + public const string EnabledResponsibleManager = "enabledResponsibleManagerPolicy"; + // + // Journalist ... + public const string Journalist = "journalistPolicy"; + public const string EnabledJournalist = "enabledJournalistPolicy"; + // + // Chief Clerk ... + public const string ChiefClerk = "chiefClerkPolicy"; + public const string EnabledChiefClerk = "enabledChiefClerkPolicy"; + // + // Chief Clerk Assistant ... + public const string ChiefClerkAssistant = "chiefClerkAssistantPolicy"; + public const string EnabledChiefClerkAssistant = "enabledChiefClerkAssistantPolicy"; + // + // Actuary ... + public const string Actuary = "actuaryPolicy"; + public const string EnabledActuary = "enabledActuaryPolicy"; + // + // Actuary Assistant ... + public const string ActuaryAssistant = "actuaryAssistantPolicy"; + public const string EnabledActuaryAssistant = "enabledActuaryAssistantPolicy"; + // + // Reporter ... + public const string Reporter = "reporterPolicy"; + public const string EnabledReporter = "enabledReporterPolicy"; + // + // Reporter Assistant ... + public const string ReporterAssistant = "reporterAssistantPolicy"; + public const string EnabledReporterAssistant = "enabledReporterAssistantPolicy"; + // + // Agent ... + public const string Agent = "agentPolicy"; + public const string EnabledAgent = "enabledAgentPolicy"; + // + // User ... + public const string User = "userPolicy"; + public const string EnabledUser = "enabledUserPolicy"; + // + // Complex Policies ... + public const string AdminOrAgent = "adminOrAgentPolicy"; + public const string EnabledAdminOrAgent = "enabledAdminOrAgentPolicy"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityHelper\XUserRoles.cs + +using System.Collections.Generic; +using xExceptions.Attributes; + +namespace xIdentityHelper +{ + public partial struct XUserRoles + { + /// + /// Must Top Role of System ... + /// + public const string ADMIN = "admin"; + + /// + /// مدیر مسئول + /// + public const string RESPONSIBLE_MANAGER = "responsible_manager"; + + /// + /// روزنامه نگار + /// + public const string JOURNALIST = "journalist"; + + /// + /// سر دبیر + /// + public const string CHIEF_CLERK = "chief_clerk"; + + /// + /// دستیار سرد بیر + /// + public const string CHIEF_CLERK_ASSISTANT = "chief_clerk_assistant"; + + /// + /// دبیر + /// + public const string ACTUARY = "actuary"; + + /// + /// دستیار دبیر + /// + public const string ACTUARY_ASSISTANT = "actuary_assistant"; + + /// + /// خبرنگار + /// + public const string REPORTER = "reporter"; + + /// + /// دستیار خبرنگار + /// + public const string REPORTER_ASSISTANT = "reporter_assistant"; + + /// + /// دستیار + /// + public const string AGENT = "agent"; + + /// + /// کاربر + /// + public const string USER = "user"; + } + + public enum XUserRole + { + /// + /// Must Top Role of System ... + /// + [StringValue("admin")] + Admin = 10, + + /// + /// مدیر مسئول + /// + [StringValue("responsible_manager")] + ResponsibleManager = 9, + + /// + /// روزنامه نگار + /// + [StringValue("journalist")] + Journalist = 8, + + /// + /// سر دبیر + /// + [StringValue("chief_clerk")] + ChiefClerk = 7, + + /// + /// دستیار سرد بیر + /// + [StringValue("chief_clerk_assistant")] + ChiefClerkAssistant = 6, + + /// + /// دبیر + /// + [StringValue("actuary")] + Actuary = 5, + + /// + /// دستیار دبیر + /// + [StringValue("actuary_assistant")] + ActuaryAssistant = 4, + + /// + /// خبرنگار + /// + [StringValue("reporter")] + Reporter = 3, + + /// + /// دستیار خبرنگار + /// + [StringValue("reporter_assistant")] + ReporterAssistant = 2, + + /// + /// دستیار + /// + [StringValue("agent")] + Agent = 1, + + /// + /// کاربر + /// + [StringValue("user")] + User = 0 + } + + public static class XUserRoleHelper + { + public static List GetAllRoles() + { + // + var result = new List + { + XUserRoles.ADMIN, + XUserRoles.RESPONSIBLE_MANAGER, + XUserRoles.JOURNALIST, + XUserRoles.CHIEF_CLERK, + XUserRoles.CHIEF_CLERK_ASSISTANT, + XUserRoles.ACTUARY, + XUserRoles.ACTUARY_ASSISTANT, + XUserRoles.REPORTER, + XUserRoles.REPORTER_ASSISTANT, + XUserRoles.AGENT, + XUserRoles.USER + }; + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Configurations\XIdentityConfiguration.cs + +using System.Collections.Generic; +using xIdentityModels.Dtos; + +namespace xIdentityModels.Configurations { + public class XIdentityConfiguration { + // + // Define All Policies which can Apply to Identity Framework + // and also create a Default instance if it's not Exists + // in AppSetting JSON file ... + public XIdentityPolicy Policy { get; set; } + + /// + /// the Security Key which used to Signing JWT Tokens + /// in Authentication Provider + /// + /// + public string IdentitySecretKey { get; set; } + + /// + /// the Issuer and Creator of Token + /// + /// + public string IdentityIssuer { get; set; } + + /// + /// the Audience and Consumer of Token + /// + /// + public string IdentityAudience { get; set; } + + /// + /// the List of Users Roles which must Provides in Identity + /// + /// + public HashSet IdentityRoles { get; set; } + + /// + /// determines All new users must Assign to which role in Registration + /// + /// + public string NewUsersRole { get; set; } + + /// + /// determines New Registered Users Confirm Email or not + /// + /// + public bool AutoConfirmNewUsersEmail { get; set; } + + /// + /// determines New Registered Users Confirm PhoneNumber or not + /// + /// + public bool AutoConfirmNewUsersPhoneNumber { get; set; } + + /// + /// determines the Registration only Can be done with Invitation or not + /// + /// + public bool RegistrationJustWithInvite { get; set; } + + /// + /// determines the Registration required confirmation with email or not + /// + /// + public bool RequireRegistrationConfirm { get; set; } + + // + public int MaxNumberOfVerificationCodeSend { get; set; } + + // + public int DeleyBetweenTwoVerificationCode { get; set; } + + public int BannedDeviceTimeout { get; set; } + + /// + /// an string Which Provide how long a action token can be valid + /// + /// + public string ActionTokenExpirationDateProvider { get; set; } + + public HashSet IdentityMessages { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Configurations\XIdentityPolicy.cs + +namespace xIdentityModels.Configurations { + /// + /// Provide the Available Policies with their Default Values + /// + public class XIdentityPolicy { + /// + /// User Account Lock Policies + /// + /// + public XIdentityPolicyLockout Lockout { get; set; } + + /// + /// User Password Policies + /// + /// + public XIdentityPolicyPassword Password { get; set; } + + /// + /// the Policies for User for Allowing to SignIn + /// + /// + public XIdentityPolicySignIn SignIn { get; set; } + + /// + /// the Policies for User + /// + /// + public XIdentityPolicyUser User { get; set; } + + /// + /// the policies which determines how to + /// populate user profile + /// + /// + public XIdentityProfile Profile { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Configurations\XIdentityPolicyLockout.cs + +namespace xIdentityModels.Configurations { + /// + /// User Account Lock Policies + /// + public class XIdentityPolicyLockout { + /// + /// The String which Provide Time Span Delais + /// between Lockout and UnLock a User Account + /// this Provided String the Parsed before Use + /// + /// + public string LockoutTimeSpanProvider { get; set; } + + /// + /// the Number of InvalidLoginAttemps which can + /// Happens to Lockout User Account + /// + /// + public byte MaxFailedAccessAttempts { get; set; } + + /// + /// determines Lockout mechanism allowed for new Users or not + /// + /// + public bool AllowedForNewUsers { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Configurations\XIdentityPolicyPassword.cs + +namespace xIdentityModels.Configurations { + /// + /// User Password Policies + /// + public class XIdentityPolicyPassword { + /// + /// determines Password must contains digits or not + /// + /// + public bool RequireDigit { get; set; } + + /// + /// determines the minimum allowed length for a password + /// + /// + public byte RequiredLength { get; set; } + + /// + /// determines the number of Unique Chars must be included in a Password + /// + /// + public byte RequiredUniqueChars { get; set; } + + /// + /// determines Password must contains LowerCase chars or not + /// + /// + public bool RequireLowercase { get; set; } + + /// + /// determines Password must contains Non-Alphabetical chars or not + /// + /// + public bool RequireNonAlphanumeric { get; set; } + + /// + /// determines Password must contains UpperCase chars or not + /// + /// + public bool RequireUppercase { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Configurations\XIdentityPolicySignIn.cs + +namespace xIdentityModels.Configurations { + /// + /// the Policies for User for Allowing to SignIn + /// + public class XIdentityPolicySignIn { + /// + /// determines only enabled users can SignIn + /// + /// + public bool RequiredEnabled { get; set; } + + /// + /// determines User must Confirm the given Email Address + /// before Login or not + /// + /// + public bool RequireConfirmedEmail { get; set; } + + /// + /// determines User must Confirm the given Phone Number + /// before Login or not + /// + /// + public bool RequireConfirmedPhoneNumber { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Configurations\XIdentityPolicyUser.cs + +using System.Collections.Generic; + +namespace xIdentityModels.Configurations { + /// + /// the Policies for User + /// + public class XIdentityPolicyUser { + /// + /// determines Allowed characters which can used in UserName + /// + /// + public string AllowedUserNameCharacters { get; set; } + + /// + /// determines the Email Address of each User must be Unique or not + /// + /// + public bool RequireUniqueEmail { get; set; } + + /// + /// minimum length of User Name + /// + /// + public byte MinLength { get; set; } + + /// + /// maximum length of user name + /// + /// + public byte MaxLength { get; set; } + + /// + /// Minimum Age for Registration + /// + /// + public int MinAgeForRegistration { get; set; } + + /// + /// Maximum age for Registration + /// + /// + public int MaxAgeForRegistration { get; set; } + + /// + /// a collection of invalid usernames + /// + /// + public ICollection InvalidUserNames { get; set; } = new HashSet (); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Configurations\XIdentityProfile.cs + +namespace xIdentityModels.Configurations { + /// + /// contains user profile configuration + /// + public class XIdentityProfile { + /// + /// determines user profile contain email or not + /// + /// + public bool ContainsEmail { get; set; } + + /// + /// determines user profile contains phone number or not + /// + /// + public bool ContainsPhoneNumber { get; set; } + + /// + /// determines user profile contains Roles collection or not + /// + /// + public bool ContainsRoles { get; set; } + + public bool ContainsDateOfBirth { get; set; } + + public bool ContainsLastLogin { get; set; } + + public bool ContainsCreationDate { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Constants\XAction.cs + +namespace xIdentityModels.Constants { + public enum XAction { + Authentication, + Invite, + Registration, + RequestMobileVerificationCode, + ConfirmMobileNumber, + RequestEmailVerificationCode, + ConfirmEmailAddress, + AddAccountInfo, + AttachProfileImage, + RequestResetPassword, + ProfileAction, + Finish, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Constants\XFriendshipState.cs + +namespace xIdentityModels.Constants { + /// + /// Represent State of Friendship between to Users + /// + public enum XFriendshipState { + None, + Pending, + Accepted, + Rejected, + Blocked, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Constants\XGender.cs + +namespace xIdentityModels.Constants { + /// + /// Determines Available Genders + /// + public enum XGender { + Male, + Female + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Constants\xIdentityModelsConstants.cs + +namespace xIdentityModels.Constants { + public class xIdentityModelsConstants { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Constants\XUserSelectBy.cs + +namespace xIdentityModels.Constants { + public enum XUserSelectBy { + NotSpecified, + ID, + Username, + Email, + MobileNumber + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Descriptors\XIdentityUserDescriptor.cs + +namespace xIdentityModels.Descriptors { + /// + /// contains default users information + /// + public class XIdentityUserDescriptor { + public string Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string UserName { get; set; } + public string Email { get; set; } + public bool EmailConfirmed { get; set; } + public string PhoneNumber { get; set; } + public bool PhoneNumberConfirmed { get; set; } + public string Password { get; set; } + public string Role { get; set; } + public bool IsEnable { get; set; } + public bool IsBanned { get; set; } + public string DateOfBirth { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\DI\XDIHelperExtension.cs + +namespace xIdentityModels.DI { + public static partial class XDIHelperExtension { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Dtos\XDiscoveryDocumentDto.cs + +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace xIdentityModels.Dtos +{ + public class XDiscoveryDocumentDto + { + [JsonProperty ("issuer")] + public string Issuer { get; set; } + + [JsonProperty ("jwks_uri")] + public string JwksUri { get; set; } + + [JsonProperty ("authorization_endpoint")] + public string AuthorizationEndpoint { get; set; } + + [JsonProperty ("token_endpoint")] + public string TokenEndpoint { get; set; } + + [JsonProperty ("userinfo_endpoint")] + public string UserinfoEndpoint { get; set; } + + [JsonProperty ("end_session_endpoint")] + public string EndSessionEndpoint { get; set; } + + [JsonProperty ("check_session_iframe")] + public string CheckSessionIframe { get; set; } + + [JsonProperty ("revocation_endpoint")] + public string RevocationEndpoint { get; set; } + + [JsonProperty ("introspection_endpoint")] + public string IntrospectionEndpoint { get; set; } + + [JsonProperty ("device_authorization_endpoint")] + public string DeviceAuthorizationEndpoint { get; set; } + + [JsonProperty ("frontchannel_logout_supported")] + public bool FrontchannelLogoutSupported { get; set; } + + [JsonProperty ("frontchannel_logout_session_supported")] + public bool FrontchannelLogoutSessionSupported { get; set; } + + [JsonProperty ("backchannel_logout_supported")] + public bool BackchannelLogoutSupported { get; set; } + + [JsonProperty ("backchannel_logout_session_supported")] + public bool BackchannelLogoutSessionSupported { get; set; } + + [JsonProperty ("scopes_supported")] + public List ScopesSupported { get; set; } + + [JsonProperty ("claims_supported")] + public List ClaimsSupported { get; set; } + + [JsonProperty ("grant_types_supported")] + public List GrantTypesSupported { get; set; } + + [JsonProperty ("response_types_supported")] + public List ResponseTypesSupported { get; set; } + + [JsonProperty ("response_modes_supported")] + public List ResponseModesSupported { get; set; } + + [JsonProperty ("token_endpoint_auth_methods_supported")] + public List TokenEndpointAuthMethodsSupported { get; set; } + + [JsonProperty ("id_token_signing_alg_values_supported")] + public List IdTokenSigningAlgValuesSupported { get; set; } + + [JsonProperty ("subject_types_supported")] + public List SubjectTypesSupported { get; set; } + + [JsonProperty ("code_challenge_methods_supported")] + public List CodeChallengeMethodsSupported { get; set; } + + [JsonProperty ("request_parameter_supported")] + public bool RequestParameterSupported { get; set; } + + [JsonProperty ("account_api")] + public string AccountApi { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Dtos\XFriendDto.cs + +using xIdentityModels.Constants; +using xModels.Base; + +namespace xIdentityModels.Dtos +{ + /// + /// All Available Friendship Types ... + /// + public enum XFriendshipType + { + Follower = 0, + Following = 1, + } + + /// + /// a DTO Class Which Represent a User Friends ... + /// + public class XFriendDto : XBaseDto + { + public string Avatar { get; set; } + public string UserId { get; set; } + public string Username { get; set; } + public string Lastname { get; set; } + public string Firstname { get; set; } + public XFriendshipType Type { get; set; } + public XFriendshipState State { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Dtos\XFriendshipInfoDto.cs + +using xModels.Base; +using xIdentityModels.Constants; + +namespace xIdentityModels.Dtos { + public class XFriendshipInfoDto : XBaseDto { + public string UserId { get; set; } + public bool IsFollower { get; set; } + public bool IsFollowing { get; set; } + public XFriendshipState FollowerState { get; set; } + public XFriendshipState FollowingState { get; set; } + public long Followers { get; set; } + public long Followings { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Dtos\XIdentityMessageDto.cs + +using xModels.Base; + +namespace xIdentityModels.Dtos { + public class XIdentityMessageDto : XBaseDto { + public string Language { get; set; } + public string ResourceTitle { get; set; } + public string TranslatedValue { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Dtos\XOpenActionDto.cs + +using System; +using xCommons.Extensions; +using xModels.Base; +using xModels.Dtos; + +namespace xIdentityModels.Dtos +{ + /// + /// a List of Available Open Actions ... + /// + public struct XOpenActions + { + public const string HandShake = "HandShake"; + public const string GetUserInfo = "GetUserInfo"; + public const string GetUserInfos = "GetUserInfos"; + } + + /// + /// Open Actions Request Model ... + /// + public class XOpenActionRequestDto : XBaseDto + { + // + public string Action { get; set; } + public string Payload { get; set; } + public string Checksum { get; set; } + public XDeviceDto Device { get; set; } + public string Timestamp { get; set; } + + /// + /// Validate Model ... + /// + /// + public bool Validate() + { + // + var result = + !Device.IsNull() && + !Timestamp.IsNull() && + !Action.IsNullOrEmpty() && + (Action == XOpenActions.HandShake || + Action == XOpenActions.GetUserInfo || + Action == XOpenActions.GetUserInfos) && + !Int64.Parse(Timestamp).FromTimestamp().IsExPired(); + + // + return result; + } + + /// + /// Checksum Digest Model ... + /// + /// + public string ToOpenActionToken() + { + // + var result = string.Empty; + if (!Validate()) + { + return result; + } + + // + var tmp = this; + tmp.Checksum = string.Empty; + + // + result = $"{tmp.ToJSON()}" + .ToMd5String(); + return result; + } + + /// + /// Validate a Token is Correspond to Model or not ... + /// + /// + /// + public bool ValidateToken(string token) + { + // + var result = + Validate() && + !token.IsNullOrEmpty() && + token == ToOpenActionToken(); + + // + return result; + } + } + + /// + /// aa Dto which used between Api's ... + /// + public class XOpenActionInnerDto : XBaseDto + { + public string Token { get; set; } + public string Payload { get; set; } + } + + /// + /// User Info Providing in Open Actions ... + /// + public class XOpenActionUserInfoDto : XBaseDto + { + public string Username { get; set; } + public string Firstname { get; set; } + public string Lastname { get; set; } + public string Avatar { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Dtos\XProfileImageDto.cs + +using System; +using Newtonsoft.Json; +using xModels.Base; + +namespace xIdentityModels.Dtos { + public class XProfileImageDto : XBaseDto { + public int Id { get; set; } + + [JsonProperty ( + "name", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string Name { get; set; } + + [JsonProperty ( + "thumb", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string Thumb { get; set; } + + [JsonProperty ( + "path", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string Path { get; set; } + + [JsonProperty ( + "thumbPath", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string ThumbPath { get; set; } + + [JsonProperty ( + "creationDate", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public DateTime CreationDate { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Dtos\XUserNameIdRequest.cs + +using System.Collections.Generic; +using xModels.Base; + +namespace xIdentityModels.Dtos { + public class XUserNameIdRequest : XBaseDto { + public IEnumerable Ids { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Dtos\XUserNameIdResponse.cs + +using xModels.Base; + +namespace xIdentityModels.Dtos { + public class XUserNameIdResponse : XBaseDto { + public string Id { get; set; } + public string UserName { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Dtos\XUserProfileDto.cs + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using xIdentityModels.Constants; +using xModels.Base; + +namespace xIdentityModels.Dtos { + public class XUserProfileDto : XBaseDto { + [JsonProperty ( + "userId", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string UserId { get; set; } + + [JsonProperty ( + "userName", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string UserName { get; set; } + + [JsonProperty ( + "email", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string Email { get; set; } + + [JsonProperty ( + "emailConfirmed", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public bool EmailConfirmed { get; set; } + + [JsonProperty ( + "phoneNumber", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string PhoneNumber { get; set; } + + [JsonProperty ( + "phoneNumberConfirmed", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public bool PhoneNumberConfirmed { get; set; } + + [JsonProperty ( + "firstName", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string FirstName { get; set; } + + [JsonProperty ( + "lastName", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string LastName { get; set; } + + [JsonProperty ( + "dateOfBirth", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public DateTime? DateOfBirth { get; set; } + + [JsonProperty ( + "creationDate", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public DateTime? CreationDate { get; set; } + + [JsonProperty ( + "lastLogin", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public DateTime? LastLogin { get; set; } + + [JsonProperty ( + "avatar", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string Avatar { get; set; } + + [JsonProperty ( + "gender", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public XGender Gender { get; set; } + + [JsonProperty ( + "bio", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string Bio { get; set; } + + [JsonProperty ( + "coverImage", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string CoverImage { get; set; } + + [JsonProperty ( + "openToSearch", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public bool OpenToSearch { get; set; } + + [JsonProperty ( + "friendshipInfo", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public XFriendshipInfoDto FriendshipInfo { get; set; } + + [JsonProperty ( + "roles", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public ICollection Roles { get; set; } = new HashSet (); + + [JsonProperty ( + "avatars", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public ICollection Avatars { get; set; } = new HashSet (); + + [JsonProperty ( + "isEnable", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public bool IsEnable { get; set; } + + [JsonProperty ( + "isBanned", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public bool IsBanned { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Extensions\DIExtensions.cs + +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xIdentityModels.Interfaces; +using xIdentityModels.Providers; + +namespace xIdentityModels.Extensions { + public static class DIExtensions { + /// + /// since registering IXUserRoleHelper need to add default instance if + /// user not provided we create this helper ... + /// + /// + public static void AddXUserRoleHandler ( + this IServiceCollection services, + IXUserRoleHelper helper = null + ) { + // + // Check if Provider is null ... + if (helper.IsNull ()) { + helper = services.GetRegisteredService (); + } + + // + // check if Provider registered or not ... + if (helper.IsNull ()) { + // + // Create Default provider ... + services.AddSingleton (new XDefaultUserRoleHelper ()); + } else { + // + // Register Given Helper ... + services.AddSingleton (helper); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Extensions\XIdentityExtensions.cs + +using System.Collections.Generic; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Models; + +namespace xIdentityModels.Extensions { + public static class XIdentityExtensions { + public static XUserSelectBy GetUserSelectByType ( + this string source, + bool forceUserSelectByParamNotEmpty = true, + bool forceUserSelectByTypeMustSpecified = false + ) { + // + // Validate Args ... + if (source.IsNullOrEmpty () && + forceUserSelectByParamNotEmpty) { + XException.InvalidArgs.Throw (); + } + + // + var result = XUserSelectBy.NotSpecified; + var userSelectByParam = source.ToNormalString (); + if (userSelectByParam.IsValidEmail ()) { + // + // Find User by Email ... + result = XUserSelectBy.Email; + } else if (userSelectByParam.IsValidMobileNumber ()) { + // + // Find User by Mobile Number ... + result = XUserSelectBy.MobileNumber; + } else if (userSelectByParam.IsGuid ()) { + // + // Find User By it's ID ... + result = XUserSelectBy.ID; + } else { + // + // UserName ... + result = XUserSelectBy.Username; + } + + // + // Validation Result ... + if (forceUserSelectByTypeMustSpecified && + result == XUserSelectBy.NotSpecified) { + XException.InvalidData.Throw (); + } + + // + return result; + } + + public static string GetUserSelectByParam ( + this XActionRequest source, + bool forceNotNull = true, + ICollection excludes = null + ) { + // + // Validate Args ... + if (source.IsNull ()) { + XException.InvalidArgs.Throw (); + } + + // + var id = source.UserId; + var userName = source.UserName; + var email = source.Email; + var phoneNumber = source.MobileNumber; + + // + if (excludes != null) { + // + if (excludes.Contains (XUserSelectBy.ID)) { + id = null; + } + if (excludes.Contains (XUserSelectBy.Email)) { + email = null; + } + if (excludes.Contains (XUserSelectBy.MobileNumber)) { + phoneNumber = null; + } + if (excludes.Contains (XUserSelectBy.Username)) { + userName = null; + } + } + + // + var selectByParam = + id.IsNullOrEmpty () ? + userName.IsNullOrEmpty () ? + email.IsNullOrEmpty () ? + phoneNumber.IsNullOrEmpty () ? null : phoneNumber : email : userName : id; + + // + // Check result ... + if (forceNotNull && + selectByParam.IsNullOrEmpty ()) { + XException.InvalidData.Throw (); + } + + // + return selectByParam; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Extensions\xIdentityModelsExtensions.cs + +using System.Collections.Generic; +using System.Linq; +using xCommons.Extensions; +using xIdentityModels.Configurations; +using xIdentityModels.Constants; +using xIdentityModels.Dtos; +using xIdentityModels.Models; + +namespace xIdentityModels.Extensions +{ + public static class xIdentityModelsExtensions + { + /// + /// Converts a User Entity to Profile Dto ... + /// + /// + /// + /// + /// + /// + /// + /// + public static XUserProfileDto ToXProfileDto( + this XUser source, + bool isAdmin = false, + bool isSameUser = false, + IEnumerable roles = null, + XFriendshipInfoDto friendship = null, + XIdentityProfile profileConfig = null + ) + { + // + XUserProfileDto result = null; + + // + if (roles == null) + { + roles = new List(); + } + + // + // Prepare Dto ... + if (!source.IsNullOrDefault()) + { + // + result = new XUserProfileDto + { + Bio = source.Bio, + UserId = source.Id, + Email = source.Email, + Roles = roles.ToList(), + Avatar = source.Avatar, + IsEnable = source.IsEnable, + IsBanned = source.IsBanned, + UserName = source.UserName, + LastName = source.LastName, + FriendshipInfo = friendship, + FirstName = source.FirstName, + LastLogin = source.LastLogin, + CoverImage = source.CoverImage, + PhoneNumber = source.PhoneNumber, + DateOfBirth = source.DateOfBirth, + CreationDate = source.CreationDate, + OpenToSearch = source.OpenToSearch, + Gender = (XGender)(int)source.Gender, + EmailConfirmed = source.EmailConfirmed, + PhoneNumberConfirmed = source.PhoneNumberConfirmed, + Avatars = source.Avatars.Select(pfi => + { + // + return new XProfileImageDto + { + Id = pfi.Id, + Name = pfi.Name, + Path = pfi.Path, + Thumb = pfi.Thumb, + ThumbPath = pfi.ThumbPath, + CreationDate = pfi.CreationDate + }; + }).ToList(), + }; + } + + // + // Apply Profiling Configuration ... + + // + bool isPassed = false; + + // + // Handle Friendship ... + isPassed = !isSameUser; + if (!isPassed) + { + result.FriendshipInfo = null; + } + + // + // Handle Email Info ... + isPassed = isAdmin || isSameUser || (!profileConfig.IsNullOrDefault() && profileConfig.ContainsEmail); + if (!isPassed) + { + result.Email = string.Empty; + result.EmailConfirmed = false; + } + + // + // Handle PhoneNumber Info ... + isPassed = isAdmin || isSameUser || (!profileConfig.IsNullOrDefault() && profileConfig.ContainsPhoneNumber); + if (!isPassed) + { + source.PhoneNumber = string.Empty; + source.PhoneNumberConfirmed = false; + } + + // + // Handle Last Login ... + isPassed = isAdmin || isSameUser || (!profileConfig.IsNullOrDefault() && profileConfig.ContainsLastLogin); + if (!isPassed) + { + result.LastLogin = null; + } + + // + // Handle Date Of Birth ... + isPassed = isAdmin || isSameUser || (!profileConfig.IsNullOrDefault() && profileConfig.ContainsDateOfBirth); + if (!isPassed) + { + result.DateOfBirth = null; + } + + // + // Handle Creation Date ... + isPassed = isAdmin || isSameUser || (!profileConfig.IsNullOrDefault() && profileConfig.ContainsCreationDate); + if (!isPassed) + { + result.CreationDate = null; + } + + // + // Handle Roles ... + isPassed = isAdmin || isSameUser || (!profileConfig.IsNullOrDefault() && profileConfig.ContainsRoles); + if (!isPassed) + { + result.Roles = new List(); + } + + // + return result; + } + + /// + /// Check Specified User Has Access to Provided Roles ... + /// + /// + /// + /// + public static bool HasAccess( + this XUserClaimsInfoDto source, + string[] roles + ) + { + // + var result = + !source.IsNullOrDefault() && + source.Roles + .Any(r => roles + .Any(ar => r.ToNormalString() + .Contains(ar.ToNormalString()))); + + // + return result; + } + + /// + /// Check Specified User Contains Specified UserSelectByParam or not ... + /// + /// + /// + /// + public static bool ContainsUserSelectByParam( + this XUser source, + string userSelectByParam + ) + { + // + var result = + !source.IsNull() && + !userSelectByParam.IsNullOrEmpty() && + (source.Id == userSelectByParam || + source.Email.ToNormalString() == userSelectByParam.ToNormalString() || + source.UserName.ToNormalString() == userSelectByParam.ToNormalString() || + source.PhoneNumber.ToNormalString() == userSelectByParam.ToNormalString()); + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Extensions\XModelExtensions.cs + +using System; +using System.Collections.Generic; +using xCommons.Extensions; +using xIdentityModels.Descriptors; +using xIdentityModels.Models; +using xIdentityModels.Navigations; + +namespace xIdentityModels.Extensions { + public static class XModelExtensions { + /// + /// /// Convert XIdentityUserDescriptor instance to XUser + /// + /// + /// + public static XUser ToXUser (this XIdentityUserDescriptor source) { + return new XUser { + Id = source.Id, + FirstName = source.FirstName, + LastName = source.LastName, + UserName = source.UserName, + Email = source.Email, + EmailConfirmed = source.EmailConfirmed, + PhoneNumber = source.PhoneNumber, + PhoneNumberConfirmed = source.PhoneNumberConfirmed, + IsEnable = true, + IsBanned = false, + CreationDate = DateTime.UtcNow, + DateOfBirth = DateTime.Parse (source.DateOfBirth) + }; + } + + /// + /// Check a User is Same as Descriptor User + /// + /// + /// + /// + public static bool IsSameAs (this XUser source, XIdentityUserDescriptor dest) { + // + if (source.IsNull () || + dest.IsNull ()) { + return false; + } + + // + var isSame = source.IsSameContent (dest, propertyWhiteList : new [] { + nameof (XIdentityUserDescriptor.FirstName), + nameof (XIdentityUserDescriptor.LastName), + nameof (XIdentityUserDescriptor.UserName), + nameof (XIdentityUserDescriptor.Email), + nameof (XIdentityUserDescriptor.EmailConfirmed), + nameof (XIdentityUserDescriptor.PhoneNumber), + nameof (XIdentityUserDescriptor.PhoneNumberConfirmed), + nameof (XIdentityUserDescriptor.IsEnable), + nameof (XIdentityUserDescriptor.IsBanned), + nameof (XIdentityUserDescriptor.DateOfBirth) + }, + propertyValueCheckers : new [] { + new KeyValuePair> ( + nameof (XIdentityUserDescriptor.DateOfBirth), + (user, descriptorUser) => { + // + var descriptorUserDateOfBirth = DateTime.Parse (descriptorUser.DateOfBirth); + return DateTime.Equals (user.DateOfBirth, descriptorUserDateOfBirth); + }) + } + ); + + // + return isSame; + } + + /// + /// Check to XDevice instance to be same or not + /// + /// + /// + /// + public static bool IsSameAs (this XDevice source, XDevice dest) { + // + if (source.IsNull () || + dest.IsNull ()) { + return false; + } + + // + var isSame = source.Os.ToNormalString () == dest.Os.ToNormalString () && + source.OsVersion.ToNormalString () == dest.OsVersion.ToNormalString () && + source.Browser.ToNormalString () == dest.Browser.ToNormalString () && + source.UserAgent.ToNormalString () == dest.UserAgent.ToNormalString () && + source.DeviceType == dest.DeviceType; + + // + return isSame; + } + + /// + /// convert UserClaimsInfo to TokenResponse ... + /// + /// + /// + public static XTokenResponse ToXTokenResponse (this XUserClaimsInfoDto source) { + // + if (source.IsNull () || + (source.AccessToken.IsNullOrEmpty () && source.RefreshToken.IsNullOrEmpty ())) { + return null; + } + + // + var result = new XTokenResponse { + AccessToken = source.AccessToken, + RefreshToken = source.RefreshToken, + ExpiresAt = source.ExpiresAt + }; + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Extensions\XTokenExtensions.cs + +using System; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Threading.Tasks; +using IdentityModel.Client; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.IdentityModel.Tokens; +using xCommons.Constants; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Models; + +namespace xIdentityModels.Extensions { + public static class XTokenExtensions { + // + #region Identity Token Extensions ... + /// + /// Convert a SecurityToken to it's Token String + /// + /// + /// + /// + public static string ToTokenString ( + this SecurityToken source, + JwtSecurityTokenHandler tokenHandler + ) { + return tokenHandler.WriteToken (source); + } + + /// + /// Validate a Token + /// + /// + /// + /// + /// + public static bool ValidateToken ( + this string token, + JwtSecurityTokenHandler tokenHandler, + TokenValidationParameters tokenValidationParams) { + // + if (token.IsNullOrEmpty ()) { + return false; + } + + // + SecurityToken tokenObj = null; + try { + tokenHandler.ValidateToken (token, tokenValidationParams, out tokenObj); + } catch { + return false; + } + + // + return tokenObj != null; + } + + /// + /// Convert a token string to Security Token + /// + /// + /// + /// + /// + public static SecurityToken ToSecurityToken ( + this string token, + JwtSecurityTokenHandler tokenHandler, + TokenValidationParameters tokenValidationParams) { + // + if (token.IsNullOrEmpty () || + !token.ValidateToken (tokenHandler, tokenValidationParams)) { + // + return null; + } + + // + // convert token from String to JWTSecurity Token ... + try { + return tokenHandler.ReadToken (token); + } catch { + return null; + } + } + + /// + /// Parse Action Token and return Corresponding RegistrationRequestDto + /// + /// + /// + /// + /// + public static XActionRequestToken ParseActionRequestToken ( + this string token, + JwtSecurityTokenHandler tokenHandler, + TokenValidationParameters tokenValidationParams) { + // + if (token.IsNullOrEmpty () || + !token.ValidateToken (tokenHandler, tokenValidationParams)) { + // + return null; + } + + // + // convert token from String to JWTSecurity Token ... + JwtSecurityToken tokenObj = null; + try { + tokenObj = tokenHandler.ReadToken (token) as JwtSecurityToken; + } catch { + return null; + } + + // + if (tokenObj == null) { + return null; + } + + // + // Extract Registered Payload Claims ... + var mCurrentStepClaim = tokenObj.Claims + .FirstOrDefault (c => c.Type == nameof (XActionRequestToken.Action)); + var mContextClaim = tokenObj.Claims + .FirstOrDefault (c => c.Type == nameof (XActionRequestToken.Context)); + var mTokenClaim = tokenObj.Claims + .FirstOrDefault (c => c.Type == nameof (XActionRequestToken.Token)); + + // + // Check All Claims to be NotNull ... + if (mCurrentStepClaim == null || + mContextClaim == null || + mTokenClaim == null) { + return null; + } + + // + // Reading all Claims Values ... + var mCurrentStepStr = mCurrentStepClaim.Value; + var mCurrentStep = 0; + try { + int.TryParse (mCurrentStepStr, out mCurrentStep); + } catch { } + + // + var mContext = mContextClaim.Value.IsNullOrEmpty () ? + null : mContextClaim.Value.FromJSON (); + var mToken = mTokenClaim.Value; + + // + // Create Instance of UserInvite ... + var result = new XActionRequestToken { + Action = (XAction) mCurrentStep, + Context = mContext, + Token = mToken + }; + + // + return result; + } + + /// + /// Check to ActionRequestContext object is same or not + /// + /// + /// + /// + public static bool IsSameAs ( + this XActionRequestContext source, + XActionRequestContext dest + ) { + // + if (source == null || + dest == null) { + return false; + } + + // + var result = source.ToJSON () == dest.ToJSON (); + + // + return result; + } + + /// + /// Convert a RegistrationRequestDto object to it's jwt token Claims + /// + /// + /// + public static Claim[] ToJwtClaims (this XActionRequestToken source) { + // + // Generate Required Fields ... + var action = source.Action; + var context = source.Context; + var token = source.Token; + + // + // Generate Claims Array ... + var claims = new Claim[] { + new Claim (nameof (XActionRequestToken.Action), ((int) action).ToString (), ClaimValueTypes.Integer), + new Claim (nameof (XActionRequestToken.Context), context == null ? "" : context.ToJSON (), ClaimValueTypes.String), + new Claim (nameof (XActionRequestToken.Token), token.IsNullOrEmpty () ? "" : token, ClaimValueTypes.String) + }; + + // + // Return Result ... + return claims; + } + + /// + /// Generate a SecurityToken based on RegistrationRequestDto object + /// + /// + /// + /// + /// + public static SecurityToken ToJwtToken ( + this XActionRequestToken source, + JwtSecurityTokenHandler tokenHandler, + SecurityTokenDescriptor tokenDescriptor) { + // + // Getting Claims ... + var claims = source.ToJwtClaims (); + tokenDescriptor.Subject = new ClaimsIdentity (claims); + + // + // Generate Token Object ... + var tokenObject = tokenHandler.CreateToken (tokenDescriptor); + + // + // Return Result ... + return tokenObject; + } + #endregion + + // + #region IdentityServer Token Extensions ... + /// + /// Retrieve Tokens From Headers + /// + /// + /// + public static XTokenResponse RetrieveTokens (this HttpRequest source) { + // + var accessToken = source.Headers[XAuthorization.Header].ToString (); + var refreshToken = source.Headers[XAuthorization.RefreshToken].ToString (); + + // + var expiresAtStr = source.Headers[XAuthorization.ExpiresAt].ToString (); + long expiresAt = expiresAtStr.ConvertTo (); + + // + if (accessToken + .ToNormalString () + .Contains (XAuthorization.TokenIdentifier.ToNormalString ())) { + accessToken = accessToken.Remove (0, XAuthorization.TokenIdentifier.Length); + } + + // + return new XTokenResponse { + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresAt = expiresAt + }; + } + + /// + /// Retrieve Tokens From HttpContext + /// + /// + /// + public static async Task RetrieveTokens (this HttpContext source) { + // + var accessToken = await source.GetTokenAsync (XAuthorization.AccessToken); + if (accessToken + .ToNormalString () + .Contains (XAuthorization.TokenIdentifier.ToNormalString ())) { + accessToken = accessToken.Remove (0, XAuthorization.TokenIdentifier.Length); + } + + var refreshToken = await source.GetTokenAsync (XAuthorization.RefreshToken); + var expiresAtStr = await source.GetTokenAsync (XAuthorization.ExpiresAt); + long expiresAt = expiresAtStr.ConvertTo (); + + // + var headerRequestTokens = source.Request.RetrieveTokens (); + + // + var result = new XTokenResponse { + AccessToken = accessToken ?? headerRequestTokens.AccessToken, + RefreshToken = refreshToken ?? headerRequestTokens.RefreshToken, + ExpiresAt = expiresAt != 0 ? expiresAt : headerRequestTokens.ExpiresAt + }; + + // + return result; + } + + /// + /// Retrieve Tokens From ActionExecutingContext + /// + /// + /// + public static async Task RetrieveTokens (this ActionExecutingContext source) { + // + var result = await source.HttpContext.RetrieveTokens (); + return result; + } + + /// + /// Retrieve Tokens From XUserInfo instance + /// + /// + /// + public static XTokenResponse RetrieveTokens (this XUserClaimsInfoDto source) { + // + var result = new XTokenResponse { + AccessToken = source.AccessToken, + RefreshToken = source.RefreshToken, + ExpiresAt = source.ExpiresAt + }; + + // + return result; + } + + /// + /// Retrieve Tokens From IHeader Dictionary + /// + /// + /// + public static XTokenResponse RetrieveTokens (this IHeaderDictionary source) { + // + var accessToken = source[XAuthorization.Header].ToString () ?? ""; + if (accessToken + .ToNormalString () + .Contains (XAuthorization.TokenIdentifier.ToNormalString ())) { + accessToken = accessToken.Remove (0, XAuthorization.TokenIdentifier.Length); + } + + // + var refreshToken = source[XAuthorization.RefreshToken].ToString () ?? ""; + + // + var expiresAtStr = source[XAuthorization.ExpiresAt].ToString () ?? ""; + var expiresAt = expiresAtStr.ConvertTo (); + + // + var result = new XTokenResponse { + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresAt = expiresAt + }; + + // + return result; + } + + /// + /// Generate XTokenResponse instance based on TokenResponse + /// + /// + /// + public static XLoginResponse CreateXLoginResponse (this TokenResponse source) { + // + if (source.IsNull () || + source.IsError) { + XException.InvalidToken.Throw (); + } + + // + var expiresAt = new DateTimeOffset (DateTime.UtcNow) + .ToUnixTimeSeconds () + + source.ExpiresIn; + + // + var result = new XLoginResponse { + AccessToken = source.AccessToken, + RefreshToken = source.RefreshToken, + ExpiresAt = expiresAt + }; + + // + return result; + } + + /// + /// Generate XTokenResponse instance based on TokenResponse + /// + /// + /// + public static XTokenResponse CreateXTokenResponse (this TokenResponse source) { + // + if (source.IsNull () || + source.IsError) { + XException.InvalidToken.Throw (); + } + + // + var expiresAt = new DateTimeOffset (DateTime.UtcNow) + .ToUnixTimeSeconds () + + source.ExpiresIn; + + // + var result = new XTokenResponse { + AccessToken = source.AccessToken, + RefreshToken = source.RefreshToken, + ExpiresAt = expiresAt + }; + + // + return result; + } + + /// + /// Convert XLoginResponse to XTokenResponse + /// + /// + /// + public static XTokenResponse ToXTokenResponse (this XLoginResponse source) { + // + if (source.IsNull ()) { + return null; + } + + // + return new XTokenResponse { + AccessToken = source.AccessToken, + RefreshToken = source.RefreshToken, + ExpiresAt = source.ExpiresAt + }; + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific IHeaderDictionary instance + /// + /// + /// + public static void SetXAuthenticationTokens ( + this IHeaderDictionary source, + XLoginResponse model, + string secret = null + ) { + // + source.Remove (XAuthorization.Header); + source.Remove (XAuthorization.RefreshToken); + source.Remove (XAuthorization.ExpiresAt); + source.Remove (XAuthorization.RevisionChecksum); + + // + source.Add (XAuthorization.Header, $"{XAuthorization.TokenIdentifier}{model.AccessToken}"); + source.Add (XAuthorization.RefreshToken, $"{model.RefreshToken}"); + source.Add (XAuthorization.ExpiresAt, $"{model.ExpiresAt}"); + + // + if (!secret.IsNullOrEmpty ()) { + var revisionChecksum = model.GetRevisionChecksum (secret); + source.Add (XAuthorization.RevisionChecksum, revisionChecksum); + } + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific IHeaderDictionary instance + /// + /// + /// + public static void SetXAuthenticationTokens ( + this IHeaderDictionary source, + XTokenResponse model, + string secret = null + ) { + // + source.Remove (XAuthorization.Header); + source.Remove (XAuthorization.RefreshToken); + source.Remove (XAuthorization.ExpiresAt); + source.Remove (XAuthorization.RevisionChecksum); + + // + source.Add (XAuthorization.Header, $"{XAuthorization.TokenIdentifier}{model.AccessToken}"); + source.Add (XAuthorization.RefreshToken, $"{model.RefreshToken}"); + source.Add (XAuthorization.ExpiresAt, $"{model.ExpiresAt}"); + + // + if (!secret.IsNullOrEmpty ()) { + var revisionChecksum = model.GetRevisionChecksum (secret); + source.Add (XAuthorization.RevisionChecksum, revisionChecksum); + } + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific IHeaderDictionary instance + /// + /// + /// + public static void SetXAuthenticationTokens ( + this HttpRequestHeaders source, + XLoginResponse model + ) { + // + source.Remove (XAuthorization.Header); + source.Remove (XAuthorization.RefreshToken); + source.Remove (XAuthorization.ExpiresAt); + source.Remove (XAuthorization.RevisionChecksum); + + // + source.Add (XAuthorization.Header, $"{XAuthorization.TokenIdentifier}{model.AccessToken}"); + source.Add (XAuthorization.RefreshToken, $"{model.RefreshToken}"); + source.Add (XAuthorization.ExpiresAt, $"{model.ExpiresAt}"); + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific IHeaderDictionary instance + /// + /// + /// + public static void SetXAuthenticationTokens ( + this HttpRequestHeaders source, + XTokenResponse model + ) { + // + source.Remove (XAuthorization.Header); + source.Remove (XAuthorization.RefreshToken); + source.Remove (XAuthorization.ExpiresAt); + source.Remove (XAuthorization.RevisionChecksum); + + // + source.Add (XAuthorization.Header, $"{XAuthorization.TokenIdentifier}{model.AccessToken}"); + source.Add (XAuthorization.RefreshToken, $"{model.RefreshToken}"); + source.Add (XAuthorization.ExpiresAt, $"{model.ExpiresAt}"); + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific HttpRequest instance + /// + /// + /// + public static void SetXAuthenticationTokens ( + this HttpRequest source, + XLoginResponse model + ) { + source.Headers.SetXAuthenticationTokens (model); + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific HttpRequest instance + /// + /// + /// + public static void SetXAuthenticationTokens ( + this HttpRequest source, + XTokenResponse model + ) { + source.Headers.SetXAuthenticationTokens (model); + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific HttpRequest instance + /// + /// + /// + public static void SetXAuthenticationTokens ( + this HttpRequestMessage source, + XLoginResponse model + ) { + source.Headers.SetXAuthenticationTokens (model); + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific HttpRequest instance + /// + /// + /// + public static void SetXAuthenticationTokens ( + this HttpRequestMessage source, + XTokenResponse model + ) { + source.Headers.SetXAuthenticationTokens (model); + } + + /// + /// Add ReNewed Authorization Tokens in Headers of + /// Specific Response. + /// Later in HttpInterceptors we can Look for AccessToken-ReNewed + /// header Value and Renew User Tokens based on it + /// + /// + /// + public static void SetXAuthenticationTokens ( + this HttpResponse source, + XLoginResponse model, + string secret + ) { + source.Headers.SetXAuthenticationTokens (model, secret); + } + + /// + /// Add ReNewed Authorization Tokens in Headers of + /// Specific Response. + /// Later in HttpInterceptors we can Look for AccessToken-ReNewed + /// header Value and Renew User Tokens based on it + /// + /// + /// + public static void SetXAuthenticationTokens ( + this HttpResponse source, + XTokenResponse model, + string secret + ) { + source.Headers.SetXAuthenticationTokens (model, secret); + } + + /// + /// Determines a Token Can Refrsh or not + /// + /// + /// + public static bool IsRefreshable (this XLoginResponse source) { + // + var containsTokenData = !source.IsNull () && + !source.AccessToken.IsNullOrEmpty () && + !source.RefreshToken.IsNullOrEmpty (); + if (!containsTokenData) { + return false; + } + + // + var current = new DateTimeOffset (DateTime.UtcNow) + .ToUnixTimeSeconds (); + + // + // var result = current > source.ExpiresAt || current > source.ExpiresAt - XAuthorization.ThresholdBeforeTokenExpiration; + var result = current > source.ExpiresAt - XAuthorization.ThresholdBeforeTokenExpiration; + + // + return result; + } + + /// + /// Determines a Token Can Refrsh or not + /// + /// + /// + public static bool IsRefreshable (this XTokenResponse source) { + // + var containsTokenData = !source.IsNull () && + !source.AccessToken.IsNullOrEmpty () && + !source.RefreshToken.IsNullOrEmpty (); + if (!containsTokenData) { + return false; + } + + // + var current = new DateTimeOffset (DateTime.UtcNow) + .ToUnixTimeSeconds (); + + // + // var result = current > source.ExpiresAt || current > source.ExpiresAt - XAuthorization.ThresholdBeforeTokenExpiration; + var result = current > source.ExpiresAt - XAuthorization.ThresholdBeforeTokenExpiration; + + // + return result; + } + + /// + /// Generate a Revision Checksum for new Tokens + /// + /// + /// + /// + public static string GetRevisionChecksum ( + this XLoginResponse source, + string secret + ) { + return (source.AccessToken + + secret + + source.RefreshToken + + secret + + source.ExpiresAt.ToString () + ) + .ToMd5String (); + } + + /// + /// Generate a Revision Checksum for new Tokens + /// + /// + /// + /// + public static string GetRevisionChecksum ( + this XTokenResponse source, + string secret + ) { + return (source.AccessToken + + secret + + source.RefreshToken + + secret + + source.ExpiresAt.ToString () + ) + .ToMd5String (); + } + + /// + /// Check an XLoginResponse instance is Valid with it's revision + /// + /// + /// + /// + /// + public static bool ValidateRevisionChecksum ( + this XLoginResponse source, + string checksum, + string secret + ) { + // + var xChecksum = source.GetRevisionChecksum (secret); + return checksum == xChecksum; + } + + /// + /// Check an XTokenResponse instance is Valid with it's revision + /// + /// + /// + /// + /// + public static bool ValidateRevisionChecksum ( + this XTokenResponse source, + string checksum, + string secret + ) { + // + var xChecksum = source.GetRevisionChecksum (secret); + return checksum == xChecksum; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Interfaces\IXUserRoleHelper.cs + +using xIdentityModels.Models; + +namespace xIdentityModels.Interfaces { + public interface IXUserRoleHelper { + bool canReadCriticalData (XUserClaimsInfoDto userInfo); + bool canDoDangerousAction (XUserClaimsInfoDto userInfo); + bool isInRole (XUserClaimsInfoDto userInfo, string role); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XActionRequest.cs + +using System; +using Microsoft.AspNetCore.Http; +using xIdentityModels.Navigations; + +namespace xIdentityModels.Models { + /// + /// represent a system wide actions + /// + public class XActionRequest { + /// + /// Client Device Locale + /// + /// user's client device local such as en-US + public string Lang { get; set; } = null; + /// + /// Requested Action Token + /// + /// a hashed content + public string ActionToken { get; set; } = null; + /// + /// user's device + /// + /// an instance of XDevice which represent user's device + public XDevice Device { get; set; } = null; + /// + /// FirstName + /// + /// users FirstName + public string FirstName { get; set; } = null; + /// + /// LastName + /// + /// user's LastName + public string LastName { get; set; } = null; + /// + /// an Optional Value which represetns User's DateOfBirth + /// + /// user's DateOfBirth + public DateTime? DateOfBirth { get; set; } = DateTime.MinValue; + /// + /// User's Id + /// + /// a GUID string which represent user's Id + public string UserId { get; set; } = null; + /// + /// UserName + /// + /// user's UserName + public string UserName { get; set; } = null; + /// + /// Email + /// + /// user's Email address + public string Email { get; set; } = null; + /// + /// MobileNumber / PhoneNumber + /// + /// user's PhoneNumber + public string MobileNumber { get; set; } = null; + /// + /// Password + /// + /// user's Password + public string Password { get; set; } = null; + /// + /// NewPassword + /// + /// a passwor string which used to Change/Reset password + public string NewPassword { get; set; } = null; + /// + /// EmailVerificationCode + /// + /// a Verification Code which assign to user's for confirm it's Email Address + public string EmailVerificationCode { get; set; } = null; + /// + /// MobileVerificationCode + /// + /// a Verification Code which assign to user's for confirm it's Phone Number + public string MobileVerificationCode { get; set; } = null; + /// + /// Thumbnail + /// + /// an instance of IFormFile which is used as user's avatar + public IFormFile Thumbnail { get; set; } + /// + /// ReturnUrl + /// + /// + /// a url string which used in many used cases for providing a way to lead users to right places. + /// + public string ReturnUrl { get; set; } = null; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XActionRequestContext.cs + +using System; +using Microsoft.AspNetCore.Http; +using xIdentityModels.Navigations; + +namespace xIdentityModels.Models { + public class XActionRequestContext { + // + public string Lang { get; set; } + + // + public string UserId { get; set; } + public string UserName { get; set; } + + // + public string MobileNumber { get; set; } + public string MobileVerificationCode { get; set; } + public bool MobileVerified { get; set; } + + // + public string Email { get; set; } + public string EmailVerificationCode { get; set; } + public bool EmailVerified { get; set; } + + // + public string FirstName { get; set; } + public string LastName { get; set; } + public DateTime DateOfBirth { get; set; } + + // + public XDevice Device { get; set; } + public IFormFile Thubmnail { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XActionRequestToken.cs + +using xCommons.Extensions; +using xIdentityModels.Constants; + +namespace xIdentityModels.Models { + public class XActionRequestToken { + public XAction Action { get; set; } + public XActionRequestContext Context { get; set; } + public string Token { get; set; } + + public void Prepare (string secret) { + Token = (Action.ToString () + + secret + + Context.ToJSON ()).ToMd5String (); + } + + public bool Validate (string secret) { + return (Action.ToString () + + secret + + Context.ToJSON ()).ToMd5String () == Token; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XActionResponse.cs + +using System; +using Newtonsoft.Json; + +namespace xIdentityModels.Models { + /// + /// represent a system wide action result + /// + public class XActionResponse { + /// + /// ActionToken + /// + /// a hashed string which carry required info for doing system actions. + [JsonProperty ( + "token", + Required = Required.Default, + NullValueHandling = NullValueHandling.Ignore)] + public string Token { get; set; } + + /// + /// ActionToken Lifetime + /// + /// an instance of DateTime which represent token expiration time + [JsonProperty ( + "expiration", + Required = Required.DisallowNull, + NullValueHandling = NullValueHandling.Ignore)] + public DateTime Expiration { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XBannedDevice.cs + +using System; +using System.ComponentModel.DataAnnotations.Schema; +using xCommons.Extensions; +using xIdentityModels.Navigations; +using xModels.Base; + +namespace xIdentityModels.Models { + public class XBannedDevice : XBaseIntIDEntity { + public DateTime BannedOn { get; set; } + + public string DeviceStr { get; set; } + + [NotMapped] + public XDevice Device { + get { + return DeviceStr.FromJSON (); + } + set { + DeviceStr = value.ToJSON (); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XLoginRequest.cs + +using System.ComponentModel.DataAnnotations; +using xIdentityModels.Navigations; + +namespace xIdentityModels.Models { + /// + /// Represent Requirements for Authenticationg a User + /// + public class XLoginRequest { + /// + /// how select an specific User + /// + /// an string value which represent how system select user such as: UserName, Email Address or MobileNumber + [Required] + public string UserSelectBy { get; set; } + + /// + /// user's password + /// + /// an string which represent user's pasword + [Required] + public string Password { get; set; } + + /// + /// user's device + /// + /// an instance of XDevice which represent user's device + public XDevice Device { get; set; } + + /// + /// user's language + /// + /// an string which represent user's language and locale, such as: en-US + public string Language { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XLoginResponse.cs + +using xIdentityModels.Dtos; + +namespace xIdentityModels.Models { + public class XLoginResponse : XTokenResponse { + public XUserProfileDto Profile { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XProfileUpdateRequest.cs + +using System; +using System.Collections.Generic; +using xIdentityModels.Constants; + +namespace xIdentityModels.Models { + public class XProfileUpdateRequest { + public string Email { get; set; } + public bool? EmailConfirmed { get; set; } + public string PhoneNumber { get; set; } + public bool? PhoneNumberConfirmed { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Avatar { get; set; } + public DateTime? DateOfBirth { get; set; } + public DateTime? CreationDate { get; set; } + public DateTime? LastLogin { get; set; } + public XGender? Gender { get; set; } + public ICollection Roles { get; set; } = new HashSet (); + public bool? IsEnable { get; set; } + public bool? IsBanned { get; set; } + public string Bio { get; set; } + public string CoverImage { get; set; } + public bool OpenToSearch { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XToken.cs + +using System.ComponentModel.DataAnnotations.Schema; +using xCommons.Extensions; +using xIdentityModels.Constants; +using xIdentityModels.Navigations; +using xModels.Base; + +namespace xIdentityModels.Models { + public class XToken : XBaseIntIDEntity { + public string Token { get; set; } + public string Hash { get; set; } + public string UserSelectByParam { get; set; } + public string DeviceStr { get; set; } + public XAction Type { get; set; } + + [NotMapped] + public XDevice Device { + get { + return DeviceStr.FromJSON (); + } + + set { + DeviceStr = value.ToJSON (); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XTokenResponse.cs + +using System.ComponentModel.DataAnnotations; + +namespace xIdentityModels.Models { + public class XTokenResponse { + [Required] + public string AccessToken { get; set; } + public string RefreshToken { get; set; } + public long ExpiresAt { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XUserClaimsInfoDto.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using xCommons.Constants; +using xCommons.Extensions; +using xCommons.Helpers; +using xExceptions.Constants; +using xIdentityModels.Constants; +using xModels.Base; + +namespace xIdentityModels.Models +{ + public class XUserClaimsInfoDto : XBaseDto + { + // + #region Properties ... + public string AccessToken { get; } + public string RefreshToken { get; } + public long ExpiresAt { get; } + public string UserId { get; } + public string UserName { get; } + public string FirstName { get; } + public string LastName { get; } + public string Picture { get; } + public string Email { get; } + public string PhoneNumber { get; } + public XGender Gender { get; } + public bool IsBanned { get; } + public bool IsEnabled { get; } + public bool EmailConfirmed { get; } + public bool PhoneNumberConfirmed { get; } + public string Bio { get; set; } + public string CoverImage { get; set; } + public bool OpenToSearch { get; set; } + public long? ExpiredOn { get; } + public long? AuthenticatedOn { get; } + public DateTime? BrithDate { get; } + public IEnumerable Issuers { get; } = new HashSet(); + public IEnumerable Audiences { get; } = new HashSet(); + public IEnumerable ClientIds { get; } = new HashSet(); + public IEnumerable Scopes { get; } = new HashSet(); + public IEnumerable Roles { get; } = new HashSet(); + #endregion + + // + public XUserClaimsInfoDto() { } + + public XUserClaimsInfoDto( + string accessToken, + string refreshToken, + long expiresAt, + IEnumerable claims + ) + { + // + // refreshToken.IsNullOrEmpty () + if (claims.IsNull() || + accessToken.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + // Attach XLogin Properties ... + AccessToken = accessToken; + RefreshToken = refreshToken; + ExpiresAt = expiresAt; + + // + if (!claims.IsNull()) + { + // + UserId = claims.FirstOrDefault(c => c.Type == XCustomClaims.UserId)?.Value ?? ""; + UserName = claims.FirstOrDefault(c => c.Type == XCustomClaims.UserName)?.Value ?? ""; + FirstName = claims.FirstOrDefault(c => c.Type == XCustomClaims.FirstName)?.Value ?? ""; + LastName = claims.FirstOrDefault(c => c.Type == XCustomClaims.LastName)?.Value ?? ""; + Picture = claims.FirstOrDefault(c => c.Type == XCustomClaims.Picture)?.Value ?? ""; + Email = claims.FirstOrDefault(c => c.Type == XCustomClaims.Email)?.Value ?? null; + PhoneNumber = claims.FirstOrDefault(c => c.Type == XCustomClaims.PhoneNumber)?.Value ?? null; + + // + Bio = claims.FirstOrDefault(c => c.Type == XCustomClaims.Bio)?.Value ?? null; + CoverImage = claims.FirstOrDefault(c => c.Type == XCustomClaims.CoverImage)?.Value ?? null; + + // + var openToSearchStr = claims.FirstOrDefault(c => c.Type == XCustomClaims.OpenToSearch)?.Value ?? null; + if (openToSearchStr.IsNullOrEmpty()) + { + OpenToSearch = false; + } + else + { + // + var openToSearch = false; + bool.TryParse(openToSearchStr, out openToSearch); + + // + OpenToSearch = openToSearch; + } + + // + // Handle Gender ... + var genders = ObjectHelper.ToEnumerableKeys(); + var genderStr = claims.FirstOrDefault(c => c.Type == XCustomClaims.Gender).Value ?? null; + if (genderStr.IsNullOrEmpty()) + { + Gender = XGender.Male; + } + else + { + Gender = genderStr.GetValue(); + } + + // + // Handle IsBanned ... + var isBannedStr = claims.FirstOrDefault(c => c.Type == XCustomClaims.IsBanned).Value ?? null; + if (isBannedStr.IsNullOrEmpty()) + { + IsBanned = false; + } + else + { + // + var isBanned = false; + Boolean.TryParse(isBannedStr, out isBanned); + + // + IsBanned = isBanned; + } + + // + // Handle IsEnabled ... + var isEnabledStr = claims.FirstOrDefault(c => c.Type == XCustomClaims.IsEnabled).Value ?? null; + if (isEnabledStr.IsNullOrEmpty()) + { + IsEnabled = false; + } + else + { + // + var isEnabled = false; + Boolean.TryParse(isEnabledStr, out isEnabled); + + // + IsEnabled = isEnabled; + } + + // + // Handle Email Confirmed ... + var emailConfirmedStr = claims.FirstOrDefault(c => c.Type == XCustomClaims.EmailVerified).Value ?? null; + if (emailConfirmedStr.IsNullOrEmpty()) + { + EmailConfirmed = false; + } + else + { + // + var emailConfirmed = false; + Boolean.TryParse(emailConfirmedStr, out emailConfirmed); + + // + EmailConfirmed = emailConfirmed; + } + + // + // Handle PhoneNumber Confirmed ... + var phoneNumberConfirmedStr = claims.FirstOrDefault(c => c.Type == XCustomClaims.PhoneNumberVerified).Value ?? null; + if (phoneNumberConfirmedStr.IsNullOrEmpty()) + { + PhoneNumberConfirmed = false; + } + else + { + // + var phoneNumberConfirmed = false; + Boolean.TryParse(phoneNumberConfirmedStr, out phoneNumberConfirmed); + + // + PhoneNumberConfirmed = phoneNumberConfirmed; + } + + // + // Handle ExpiredOn ... + var expiredOnStr = claims.FirstOrDefault(c => c.Type == XCustomClaims.ExpiredOn).Value ?? null; + if (expiredOnStr.IsNullOrEmpty()) + { + ExpiredOn = null; + } + else + { + // + long expiredOn = 0; + long.TryParse(expiredOnStr, out expiredOn); + + // + ExpiredOn = expiredOn; + } + + // + // Handle AuthenticatedOn ... + var authenticatedOnStr = claims.FirstOrDefault(c => c.Type == XCustomClaims.AuthenticatedOn).Value ?? null; + if (authenticatedOnStr.IsNullOrEmpty()) + { + AuthenticatedOn = null; + } + else + { + // + long authenticatedOn = 0; + long.TryParse(authenticatedOnStr, out authenticatedOn); + + // + AuthenticatedOn = authenticatedOn; + } + + // + // Handle BrithDate ... + var brithDateStr = claims.FirstOrDefault(c => c.Type == XCustomClaims.BrithDate)?.Value ?? ""; + if (brithDateStr.IsNullOrEmpty()) + { + // BrithDate = null; + } + else + { + // + DateTime bDate; + DateTime.TryParse(brithDateStr, out bDate); + + // + BrithDate = bDate; + } + + // + // Collections ... + Issuers = claims.Where(c => c.Type == XCustomClaims.Issuer)? + .Select(c => c.Value) ?? new HashSet(); + Audiences = claims.Where(c => c.Type == XCustomClaims.Audience)? + .Select(c => c.Value) ?? new HashSet(); + ClientIds = claims.Where(c => c.Type == XCustomClaims.ClientId)? + .Select(c => c.Value) ?? new HashSet(); + Scopes = claims.Where(c => c.Type == XCustomClaims.Scope)? + .Select(c => c.Value) ?? new HashSet(); + Roles = claims.Where(c => c.Type == XCustomClaims.Role)? + .Select(c => c.Value) ?? new HashSet(); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Models\XVerificationRequest.cs + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using xCommons.Extensions; +using xIdentityModels.Navigations; +using xModels.Base; + +namespace xIdentityModels.Models { + public class XVerificationRequest : XBaseIntIDEntity { + + [Required] + public string VerificationCode { get; set; } + + public int NumberOfTries { get; set; } + public DateTime LastTryOn { get; set; } + + [NotMapped] + public XActionRequestToken Request { + get { + return RequestStr.FromJSON (); + } + set { + RequestStr = value.ToJSON (); + } + } + + public string RequestStr { get; set; } + + [NotMapped] + public XDevice Device { + get { + return DeviceStr.FromJSON (); + } + set { + DeviceStr = value.ToJSON (); + } + } + + public string DeviceStr { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Navigations\XDevice.cs + +using System.ComponentModel.DataAnnotations.Schema; +using xCommons.Constants; +using xModels.Base; + +namespace xIdentityModels.Navigations { + /// + /// Represent a Client Device which used by User + /// + public class XDevice : XBaseIntIDEntity { + /// + /// Operating System + /// + /// device operating system identifier + public string Os { get; set; } + /// + /// Operating System Version + /// + /// which version of operatin system is in use + public string OsVersion { get; set; } + /// + /// Device Browser + /// + /// the Browser identifier + public string Browser { get; set; } + /// + /// Browser Engine Agent + /// + /// browser engine Agent + public string UserAgent { get; set; } + /// + /// Type of client Device + /// + /// represent device type XDeviceType + public XDeviceType DeviceType { get; set; } + + // + #region User ... + /// + /// UserId + /// + /// Represent this Devie belong to which User Identifier + public string UserId { get; set; } + /// + /// User + /// + /// Represent Device User XUser + [ForeignKey (nameof (UserId))] + public virtual XUser User { get; set; } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Navigations\XFriendshipFollower.cs + +using System.ComponentModel.DataAnnotations.Schema; +using xIdentityModels.Constants; +using xModels.Base; + +namespace xIdentityModels.Navigations { + /// + /// represent a Follower of specific User + /// + public class XFriendshipFollower : XBaseIntIDEntity { + /// + /// XFriendshipState + /// + /// state of Friendship at current moment XFriendshipState + public XFriendshipState State { get; set; } + + // + #region User ... + /// + /// UserId + /// + /// source User's Id + public string UserId { get; set; } + /// + /// User + /// + /// source User XUser + [ForeignKey (nameof (UserId))] + public virtual XUser User { get; set; } + #endregion + + // + #region Dest User ... + /// + /// DestId + /// + /// destination User's Id + public string DestId { get; set; } + /// + /// Dest + /// + /// dest User XUser + [ForeignKey (nameof (DestId))] + public virtual XUser Dest { get; set; } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Navigations\XFriendshipFollowing.cs + +using System.ComponentModel.DataAnnotations.Schema; +using xIdentityModels.Constants; +using xModels.Base; + +namespace xIdentityModels.Navigations { + /// + /// represent a Following of specific User + /// + public class XFriendshipFollowing : XBaseIntIDEntity { + /// + /// XFriendshipState + /// + /// state of Friendship at current moment XFriendshipState + public XFriendshipState State { get; set; } + + // + #region User ... + /// + /// UserId + /// + /// source User's Id + public string UserId { get; set; } + /// + /// User + /// + /// source User XUser + [ForeignKey (nameof (UserId))] + public virtual XUser User { get; set; } + #endregion + + // + #region Dest User ... + /// + /// DestId + /// + /// destination User's Id + public string DestId { get; set; } + /// + /// Dest + /// + /// dest User XUser + [ForeignKey (nameof (DestId))] + public virtual XUser Dest { get; set; } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Navigations\XProfileImage.cs + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using xModels.Base; + +namespace xIdentityModels.Navigations { + /// + /// Represent a User's Avatar Entity + /// + public class XProfileImage : XBaseIntIDEntity { + /// + /// UserId + /// + /// represent current Avatar belongs to which User + public string UserId { get; set; } + /// + /// User + /// + /// User instance XUser + [ForeignKey (nameof (UserId))] + public XUser User { get; set; } + /// + /// Name + /// + /// Avatar's file name + [Required] + [StringLength (255)] + public string Name { get; set; } + /// + /// Thumb + /// + /// Avatar's thumbnail file's name + [Required] + [StringLength (255)] + public string Thumb { get; set; } + /// + /// Path + /// + /// Avatar's Image path on Profile Server + [Required] + [StringLength (255)] + public string Path { get; set; } + /// + /// ThumbPath + /// + /// Avatar's Thumbnail Image path on Profile Service + [Required] + [StringLength (255)] + public string ThumbPath { get; set; } + /// + /// CreationDate + /// + /// Upload Time + public DateTime CreationDate { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\Providers\XDefaultUserRoleHelper.cs + +using System.Linq; +using xCommons.Extensions; +using xIdentityModels.Models; +using xIdentityModels.Interfaces; + +namespace xIdentityModels.Providers { + public class XDefaultUserRoleHelper : IXUserRoleHelper { + public bool canReadCriticalData (XUserClaimsInfoDto userInfo) { + return isInRole (userInfo, "admin"); + } + + public bool canDoDangerousAction (XUserClaimsInfoDto userInfo) { + return isInRole (userInfo, "admin"); + } + + public bool isInRole (XUserClaimsInfoDto userInfo, string role) { + return userInfo.Roles + .Any (r => + r.ToNormalString () == role.ToNormalString () + ); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\XApiToken.cs + +using System.ComponentModel.DataAnnotations.Schema; +using xCommons.Extensions; +using xModels.Base; +using xIdentityModels.Models; + +namespace xIdentityModels { + public class XApiToken : XBaseIntIDEntity { + public string Content { get; set; } + + [NotMapped] + public XTokenResponse Tokens { + get { return Content.IsNullOrEmpty () ? null : Content.FromJSON (); } + set { Content = value.ToJSON (); } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityModels\XUser.cs + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.AspNetCore.Identity; +using xIdentityModels.Constants; +using xIdentityModels.Navigations; + +namespace xIdentityModels +{ + /// + /// represnt a User of System + /// + public partial class XUser : IdentityUser + { + // + #region Properties ... + /// + /// FirstName + /// + /// user's FirstName + [Required] + [StringLength(50)] + public string FirstName { get; set; } + /// + /// LastName + /// + /// user's LastName + [Required] + [StringLength(100)] + public string LastName { get; set; } + /// + /// User is Enabled or not + /// + /// + /// system was restrict Disabled Users access to Many Resources + /// + /// boolean value represent User is Enable or not + public bool IsEnable { get; set; } + /// + /// User is Banned or not + /// + /// + /// system was reject Banned Users access to Almost all Resources + /// + /// boolean value represent User is Banned or not + public bool IsBanned { get; set; } + /// + /// DateOfBirth + /// + /// user's DateOfBirth + [Required] + public DateTime DateOfBirth { get; set; } + /// + /// CreationDate + /// + /// user's Creation/Registration Date + [Required] + public DateTime CreationDate { get; set; } + /// + /// LastLogin + /// + /// user's Last Successfull Login Date + public DateTime? LastLogin { get; set; } + /// + /// ProfileImage/Avatar + /// + /// user's current Avatar + public string Avatar { get; set; } + /// + /// Gender + /// + /// user's Gender + [Required] + public XGender Gender { get; set; } + /// + /// User Biography ... + /// + /// + public string Bio { get; set; } + /// + /// User Bio Cover Image ... + /// + /// + public string CoverImage { get; set; } + /// + /// Can Discover User ... + /// + /// + public bool OpenToSearch { get; set; } + #endregion + + // + #region Navigation Properties ... + /// + /// User's Devices + /// + /// XDevice + /// a Collection of all assigned Devices to User + [InverseProperty("User")] + public virtual ICollection Devices { get; set; } = new HashSet(); + /// + /// User's Avatars + /// + /// XProfileImage + /// a Collection of all assigned Avatars to User + [InverseProperty("User")] + public virtual ICollection Avatars { get; set; } = new HashSet(); + /// + /// User's Followers + /// + /// XFriendshipFollower + /// a Collection of all User's Followers + [InverseProperty("User")] + public virtual ICollection Followers { get; set; } = new HashSet(); + /// + /// User's Followings + /// + /// XFriendshipFollowing + /// a Collection of all User's Followings + [InverseProperty("User")] + public virtual ICollection Followings { get; set; } = new HashSet(); + /// + /// User's Roles + /// + /// a Collection of all User's Roles in System + public virtual ICollection> Roles { get; set; } = new HashSet>(); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Configuration\XIdentityServiceConfiguration.cs + +using xIdentityService.Constants; + +namespace xIdentityService.Configuration { + public partial class XIdentityServiceConfiguration { + public string Authority { get; set; } + public string ApiName { get; set; } + public string ApiSecret { get; set; } + public string ClientId { get; set; } + public string ClientSecret { get; set; } + public string XPoweredValue { get; set; } + public XTokenType TokenType { get; set; } + public string XRevisionSecretKey { get; set; } + public int DefaultClientTimeout { get; set; } = 360; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Configuration\XProxyConfiguration.cs + +using System.Collections.Generic; +using xModels.Base; + +namespace xIdentityService.Configuration { + /// + /// XProxy Middleware Configuration ... + /// + public class XProxyConfiguration { + /// + /// Proxy Host Address for Clients Which Use XProxy Server ... + /// + /// + public string Host { get; set; } + + /// + /// if EnableXPoweredByAthorization property setted to true, this property determines + /// the header must be this value ... + /// + /// + public string XPoweredValue { get; set; } + + /// + /// Determines Proxy Http Handler Check SSL or not ... + /// + /// + public bool DisableSSLCheck { get; set; } + + /// + /// Determines Http Client Handler Allow Auto Redirect or not ... + /// + /// + public bool AllowAutoRedirect { get; set; } + + /// + /// Determines Proxy Server Log Received Requests or not ... + /// + /// + public bool EnableLogging { get; set; } + + /// + /// DefineRoutes for Handling ... + /// + /// + public HashSet Routes { get; set; } + } + + /// + /// Describe a Proxy Route ... + /// + public class XProxyRouteDescriptor : XBaseDescriptor { + /// + /// Determines which route can handled for Proxyfing ... + /// + /// + public string Route { get; set; } + + /// + /// Determines Proxy Server Handle Athorization or not ... + /// + /// + public bool EnableAuthorization { get; set; } + + /// + /// Determines Which Scopes allowed to this route if authorizations enabled ... + /// + /// + public HashSet AllowedScopes { get; set; } + + /// + /// Determines Which Roles allowed to this route if authorizations enabled ... + /// + /// + public HashSet AllowedRoles { get; set; } + + /// + /// Determines XProxy Can Check XPowered By Authorization or not ... + /// + /// + public bool EnableXPoweredByAthorization { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Constants\ConfigurationNodeNames.cs + +namespace xIdentityService.Constants { + public partial struct ConfigurationNodeNames { + public const string XPROXY_CONFIGURATION_NODE_NAME = "ProxyConfiguration"; + public const string IDENTITY_SERVICE_NODE_NAME = "IdentityServiceConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Constants\XApiAccountEndpoint.cs + +using xExceptions.Attributes; + +namespace xIdentityService.Constants +{ + public enum XApiAccountEndpoint + { + // + #region Authentication ... + [StringValue("Account/DiscoveryDocument")] + DiscoveryDocument, + + [StringValue("Account/RequestScopeAccessToken")] + RequestScopeAccessToken, + + [StringValue("Account/Authenticate")] + Authenticate, + + [StringValue("Account/Login")] + Login, + + [StringValue("Account/RefreshTokens")] + RefreshTokens, + + [StringValue("Account/ChangePassword")] + ChangePassword, + + [StringValue("Account/ResetPassword")] + ResetPassword, + #endregion + + // + #region Proile ... + [StringValue("Account/Profile/{ids}/GetNames")] + GetNames, + + [StringValue("Account/Profile/GetNameIds")] + GetNameIds, + + [StringValue("Account/Profile/{userSelectByParam}")] + GetProfile, + + [StringValue("Account/Profile/Query")] + QueryProfiles, + + [StringValue("Account/Profile/Avatars/Query/{userSelectByParam}")] + QueryAvatars, + + [StringValue("Account/Profile/Update/{userSelectByParam}")] + UpdateProfile, + + [StringValue("Account/Profile/{userSelectByParam}")] + FullUpdateProfile, + + [StringValue("Account/Profile/Avatar")] + AddAvatar, + + [StringValue("Account/Profile/Avatars")] + AddAvatars, + + [StringValue("Account/Profile/Avatars/{ids}")] + RemoveAvatars, + + [StringValue("Account/Profile/Avatars/{id}/Set")] + SetAvatar, + #endregion + + // + #region Search ... + [StringValue("Account/OpenToSearch/Query")] + QueryOpenToSearchProfiles, + #endregion + + // + #region Registration ... + [StringValue("Account/CanRegisterUserName/{userName}")] + CanRegisterUserName, + + [StringValue("Account/CanRegisterMobileNumber/{mobileNumber}")] + CanRegisterMobileNumber, + + [StringValue("Account/CanRegisterEmail/{email}")] + CanRegisterEmail, + + [StringValue("Account/IsConfirmedEmail")] + IsConfirmedEmail, + + [StringValue("Account/IsConfirmedMobile")] + IsConfirmedMobile, + + [StringValue("Account/ConfirmRegistration")] + ConfirmRegistration, + + [StringValue("Account/ConfirmMobileNumber")] + ConfirmMobileNumber, + + [StringValue("Account/ConfirmEmailAddress")] + ConfirmEmailAddress, + + [StringValue("Account/RequestConfirmMobile")] + RequestConfirmMobile, + + [StringValue("Account/RequestConfirmEmail")] + RequestConfirmEmail, + + [StringValue("Account/RequestResetPassword")] + RequestResetPassword, + + [StringValue("Account/RequestConfirmRegistration")] + RequestConfirmRegistration, + + [StringValue("Account/InviteUser")] + InviteUser, + + [StringValue("Account/RequestRegistration")] + RequestRegistration, + + [StringValue("Account/AddAccountInfo")] + AddAccountInfo, + + [StringValue("Account/AttachProfileImage")] + AttachProfileImage, + + [StringValue("Account/FinishRegistration")] + FinishRegistration, + #endregion + + // + #region Friendship ... + [StringValue("Account/Friendship/{destUser}/Follow")] + Follow, + + [StringValue("Account/Friendship/{destUser}/Cancel")] + Cancel, + + [StringValue("Account/Friendship/{destUser}/UnFollowFollower")] + UnFollowFollower, + + [StringValue("Account/Friendship/{destUser}/UnFollowFollowing")] + UnFollowFollowing, + + [StringValue("Account/Friendship/{destUser}/Block")] + Block, + + [StringValue("Account/Friendship/{destUser}/UnBlock")] + UnBlock, + + [StringValue("Account/Friendship/{destUser}/AcceptRequest")] + AcceptRequest, + + [StringValue("Account/Friendship/{destUser}/RejectRequest")] + RejectRequest, + + [StringValue("Account/Friendship/{destUser}/FriendshipInfo")] + FriendshipInfo, + + [StringValue("Account/Friendship/{destUser}/IsFollower")] + IsFollower, + + [StringValue("Account/Friendship/{destUser}/FollowerState")] + FollowerState, + + [StringValue("Account/Friendship/{destUser}/Follower")] + Follower, + + [StringValue("Account/Friendship/Followers")] + Followers, + + [StringValue("Account/Friendship/AllFollowers")] + AllFollowers, + + [StringValue("Account/Friendship/FollowersList")] + FollowersList, + + [StringValue("Account/Friendship/QueryFollowers")] + QueryFollowers, + + [StringValue("Account/Friendship/{destUser}/IsFollowing")] + IsFollowing, + + [StringValue("Account/Friendship/{destUser}/FollowingState")] + FollowingState, + + [StringValue("Account/Friendship/{destUser}/Following")] + Following, + + [StringValue("Account/Friendship/Followings")] + Followings, + + [StringValue("Account/Friendship/AllFollowings")] + AllFollowings, + + [StringValue("Account/Friendship/FollowingsList")] + FollowingsList, + + [StringValue("Account/Friendship/QueryFollowings")] + QueryFollowings, + #endregion + + // + #region Admin ... + [StringValue("Account/Ban")] + Ban, + + [StringValue("Account/UnBan")] + UnBan, + + [StringValue("Account/IsBanned/{userSelectByParam}")] + IsBanned, + #endregion + + // + #region Open Actions ... + [StringValue("Account/Open")] + Open, + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Constants\XAuthenticationScheme.cs + +using xExceptions.Attributes; + +namespace xIdentityService.Constants { + public enum XAuthenticationScheme { + [StringValue ("token")] + XToken, + [StringValue ("introspection")] + XIntrospection, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Constants\XHeader.cs + +using xExceptions.Attributes; + +namespace xIdentityService.Constants +{ + /// + /// All Available Headers ... + /// + public enum XHeader + { + /// + /// Hub Connection ID Header Keys ... + /// + [StringValue("requestID")] + ConnectionId, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Constants\XParam.cs + +using xExceptions.Attributes; + +namespace xIdentityService.Constants { + public enum XParam { + [StringValue ("Content-Type")] + XContentType, + + [StringValue ("{boundary}")] + XBoundary, + + [StringValue ("{userSelectByParam}")] + XUserSelectByParam, + + [StringValue ("{destUser}")] + XDestUser, + + [StringValue ("{id}")] + XId, + + [StringValue ("{ids}")] + XIds, + + [StringValue ("Origin")] + XOrigin, + + [StringValue ("{userName}")] + XUserName, + + [StringValue ("{mobileNumber}")] + XMobileNumber, + + [StringValue ("{email}")] + XEmail, + + [StringValue ("role")] + XRole, + + [StringValue ("forceRole")] + XForceRole, + + [StringValue ("IsRefreshed")] + XIsRefreshed, + + [StringValue ("file")] + XFile, + + [StringValue ("files")] + XFiles, + + [StringValue ("actionToken")] + XActionToken, + + [StringValue ("request")] + XRequest, + + [StringValue ("scope")] + XScope, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Constants\XProxyConstants.cs + +namespace xIdentityService.Constants { + public struct XProxyConstants { + public const string XPROXY_HTTP_CLIENT = "XProxyHttpClient"; + public const string XPROXY_FORWARD_HEADER = "xForwardHeader"; + public const string XPROXY_ACCEPTED_HEADERS = "xAcceptedHeaders"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Constants\XResponseContentTypes.cs + +namespace xIdentityService.Constants { + public struct XResponseContentTypes { + public const string HTML = "text/html"; + public const string JSON = "application/json"; + public const string JavaScript = "text/javascript"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Constants\XTokenType.cs + +namespace xIdentityService.Constants { + public enum XTokenType { + // + // Summary: + // Self-contained Json Web Token + Jwt, + // + // Summary: + // Reference token + Reference + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Controllers\XBaseIdentityApiController.cs + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Attributes; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; + +namespace xIdentityService.Controllers +{ + /// + /// Base Identity Api Controller + /// + [ApiController] + [RequireXPowered(true)] + [Route("api/[controller]")] + [Produces("application/json")] + public abstract class XBaseIdentityApiController : XBaseIdentityController + { + protected XBaseIdentityApiController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Controllers\XBaseIdentityApiV1Controller.cs + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Attributes; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; + +namespace xIdentityService.Controllers +{ + /// + /// Base Identity Api V1 Controller + /// + [ApiController] + [ApiVersion("1.0")] + [RequireXPowered(true)] + [Route("api/v{version:apiVersion}/[controller]")] + public abstract class XBaseIdentityApiV1Controller : XBaseIdentityApiController + { + protected XBaseIdentityApiV1Controller( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Controllers\XBaseIdentityController.cs + +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Constants; +using xCommons.Controllers; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Models; +using xIdentityService.Constants; +using xIdentityService.Interfaces; + +namespace xIdentityService.Controllers +{ + public abstract class XBaseIdentityController : XBaseController + { + // + public IXIdentityProvider IdentityProvider { get; } + + protected XBaseIdentityController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { + IdentityProvider = identityProvider; + } + + // + #region NonActions ... + /// + /// Retrieve Connection ID for Hubs ... + /// + /// + [NonAction] + public string GetConnectionId() + { + // + var result = ""; + + // + var headerKey = XHeader.ConnectionId.GetStringValue(); + + // + // Check if Connection ID is Provided ... + if (Request.Headers.ContainsKey(headerKey)) + { + result = Request.Headers[headerKey]; + } + + // + return result; + } + + /// + /// Retrieve User Identifier Base on XActionRequest + /// + /// + /// + /// + /// + [NonAction] + public string GetUserSelectByParam( + XActionRequest model, + bool forceNotNull = true, + ICollection excludes = null + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(model); + + // + var result = IdentityProvider.GetUserSelectByParam( + model, + forceNotNull, + excludes + ); + + // + return result; + } + + /// + /// Retrieve Access Token + /// + /// + [NonAction] + public async Task GetAccessToken() + { + // + var accessToken = Request.Headers[XAuthorization.Header].ToString(); + if (accessToken.IsNullOrEmpty()) + { + // + accessToken = await HttpContext + .GetTokenAsync(XAuthorization.AccessToken); + } + + // + if (accessToken + .ToNormalString() + .Contains(XAuthorization.TokenIdentifier + .ToNormalString())) + { + // + accessToken = accessToken + .Remove(0, XAuthorization.TokenIdentifier.Length); + } + + // + return accessToken; + } + + /// + /// Retrieve Refresh Token + /// + /// + [NonAction] + public async Task GetRefreshToken() + { + // + var refreshToken = Request.Headers[XAuthorization.RefreshToken].ToString(); + if (refreshToken.IsNullOrEmpty()) + { + // + refreshToken = await HttpContext + .GetTokenAsync(XAuthorization.RefreshToken); + } + + // + return refreshToken; + } + + /// + /// Retrieve Access Token Expiration Date + /// + /// + [NonAction] + public async Task GetTokenExpiresAt() + { + // + var expiresAtStr = Request.Headers[XAuthorization.ExpiresAt].ToString(); + if (expiresAtStr.IsNullOrEmpty()) + { + // + expiresAtStr = await HttpContext + .GetTokenAsync(XAuthorization.ExpiresAt); + } + + // + var expiresAt = expiresAtStr.ConvertTo(); + + // + return expiresAt; + } + + /// + /// Retrieve All Required Tokens + /// + /// + [NonAction] + public async Task RetrieveTokensAsXLoginResponse() + { + // + var accessToken = await GetAccessToken(); + var refreshToken = await GetRefreshToken(); + var expiresAt = await GetTokenExpiresAt(); + + // + return new XLoginResponse + { + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresAt = expiresAt + }; + } + + /// + /// Retrieve All Required Tokens + /// + /// + [NonAction] + public async Task RetrieveTokensAsXTokenResponse() + { + // + var accessToken = await GetAccessToken(); + var refreshToken = await GetRefreshToken(); + var expiresAt = await GetTokenExpiresAt(); + + // + return new XTokenResponse + { + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresAt = expiresAt + }; + } + + /// + /// Retrive UserInfo + /// + /// + [NonAction] + public async Task GetUserInfo() + { + // + var claims = User.Claims ?? null; + if (!claims.HasChild()) + { + return null; + } + + var xTokens = await RetrieveTokensAsXTokenResponse(); + var result = new XUserClaimsInfoDto( + xTokens.AccessToken, + xTokens.RefreshToken, + xTokens.ExpiresAt, + User.Claims + ); + + // + return result; + } + + /// + /// Validate User Authenticated and Retrieve User Info + /// + /// + [NonAction] + public async Task ValidateAndGetUserInfo() + { + // + if (!User.Identity.IsAuthenticated) + { + XException.NotAuthorized.Throw(); + } + + // + var result = await GetUserInfo(); + if (result.IsNull()) + { + XException.NotAuthorized.Throw(); + } + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\DI\XDIHelperExtension.cs + +using System; +using IdentityModel.AspNetCore.OAuth2Introspection; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Authorization; +using xCommons.Extensions; +using xIdentityService.Configuration; +using xIdentityService.Constants; +using xIdentityService.Interfaces; +using xIdentityService.Providers; +using xIdentityService.Security; + +namespace xIdentityService.DI { + public static partial class XDIHelperExtension { + /// + /// Extract Module Configuration Structure from IConfiguration interface + /// + /// + /// + public static XIdentityServiceConfiguration GetXIdentityServiceConfiguration (this IConfiguration config) { + // + var xIdentityServiceConfigSection = config.GetSection (ConfigurationNodeNames.IDENTITY_SERVICE_NODE_NAME); + return xIdentityServiceConfigSection.Get (); + } + + /// + /// Register Service and All it's Requirements + /// + /// + /// + public static void AddXIdentityService ( + this IServiceCollection services, + IConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Singleton + ) { + // + var identityConfiguration = configuration.GetXIdentityServiceConfiguration (); + AddIdentityService ( + services, + identityConfiguration, + lifeTime + ); + } + + /// + /// Register Service and All it's Requirements + /// + /// + /// + public static void AddXIdentityService ( + this IServiceCollection services, + XIdentityServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Singleton + ) { + AddIdentityService (services, configuration, lifeTime); + } + + /// + /// Register Custome Token Provider + /// + /// + /// + public static void AddXTokenProvider ( + this IServiceCollection services, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + where T : IXTokenProvider { + // + Console.WriteLine ("IdentityService: Add Custom Token Provider ..."); + services.Add (new ServiceDescriptor (typeof (IXTokenProvider), typeof (T), lifetime)); + } + + /// + /// Use Authentication + /// + /// + public static void UseXIdentityService (this IApplicationBuilder app) { + app.UseAuthentication (); + } + + // + #region Private ... + private static void AddIdentityService ( + this IServiceCollection services, + XIdentityServiceConfiguration config, + ServiceLifetime lifeTime = ServiceLifetime.Singleton + ) { + // + services.AddSingleton (config); + + // + var builder = services + .AddAuthentication (XAuthenticationScheme.XToken.GetStringValue ()); + + // + // Add JWT Handler ... + builder.AddJwtBearer ( + XAuthenticationScheme.XToken.GetStringValue (), options => { + // + options.Authority = config.Authority; + options.TokenValidationParameters.ValidateAudience = false; + options.TokenValidationParameters.ValidTypes = new [] { "at+jwt" }; + + // + options.ForwardDefaultSelector = context => { + // + var path = context.Request.Path.Value; + + // + var fromHeader = TokenRetrieval.FromAuthorizationHeader (); + var fromQuery = TokenRetrieval.FromQueryString (); + var bearerToken = fromHeader (context.Request) ?? fromQuery (context.Request); + + // + var result = XAuthenticationScheme.XToken.GetStringValue (); + if (!bearerToken.IsNull () && !bearerToken.Contains (".")) { + result = XAuthenticationScheme.XIntrospection.GetStringValue (); + } + + // + return result; + }; + }); + + // + // Add OAuthIntrospect ... + builder.AddOAuth2Introspection ( + XAuthenticationScheme.XIntrospection.GetStringValue (), options => { + // + options.Authority = config.Authority; + options.ClientId = config.ApiName; + options.ClientSecret = config.ClientSecret; + + // + options.TokenRetriever = new Func (req => { + // + var fromHeader = TokenRetrieval.FromAuthorizationHeader (); + var fromQuery = TokenRetrieval.FromQueryString (); + + // + var bearerToken = fromHeader (req) ?? fromQuery (req); + + // + return bearerToken; + }); + }); + + // + // Register Authorization Handler ... + services.AddSingleton (); + Console.WriteLine ("IdentityService: register Authorization Handlers ..."); + + // + // Register Identity Provider ... + services.Add (new ServiceDescriptor (typeof (IXIdentityProvider), typeof (XIdentityProvider), lifeTime)); + Console.WriteLine ("IdentityService: Register IdentityProvider ..."); + + // + // Try To Register InMemoryTokenProvier if it's not Provided ... + IXTokenProvider xTokenProvider = null; + try { + xTokenProvider = services.GetRegisteredService (); + } catch { } + if (xTokenProvider.IsNull ()) { + // + Console.WriteLine ("IdentityService: there is no Custom Token Provider, Adding InMemoryTokenProvider instead ..."); + services.Add (new ServiceDescriptor (typeof (IXTokenProvider), typeof (XInMemoryTokenProvider), lifeTime)); + } + + // + // Adding Security Provider ... + services.Add (new ServiceDescriptor (typeof (IXSecurityProvider), typeof (XSecurityProvider), lifeTime)); + Console.WriteLine ("IdentityService: Register Security Provider ..."); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Extensions\AuthorizationPolicyBuilderExtensions.cs + +using IdentityModel; +using Microsoft.AspNetCore.Authorization; + +namespace xIdentityService.Extensions { + public static class AuthorizationPolicyBuilderExtensions { + /// + /// Adds a policy to check for required scopes. + /// + /// + /// List of any required scopes. The token must contain at least one of the listed scopes. + /// + public static AuthorizationPolicyBuilder RequireScope (this AuthorizationPolicyBuilder builder, params string[] scope) { + return builder.RequireClaim (JwtClaimTypes.Scope, scope); + } + } + + /// + /// Helper for creating scope-related policies + /// + public static class ScopePolicy { + /// + /// Creates a policy to check for required scopes. + /// + /// List of any required scopes. The token must contain at least one of the listed scopes. + /// + public static AuthorizationPolicy Create (params string[] scopes) { + return new AuthorizationPolicyBuilder () + .RequireScope (scopes) + .Build (); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Extensions\HttpContextExtensions.cs + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Constants; +using xCommons.Extensions; +using xIdentityModels.Models; +using xIdentityService.Constants; + +namespace xIdentityService.Extensions { + public static class HttpContextExtensions { + /// + /// Retrieve Authentication Service ... + /// + /// + /// + public static IAuthenticationService GetAuthenticationService (this HttpContext context) { + // + var result = context.RequestServices + .GetRequiredService (); + + // + return result; + } + + /// + /// Retrieve Specific Token from HttpContext ... + /// + /// + /// + /// + /// + public static async Task GetXTokenAsync ( + this HttpContext context, + string tokenName, + XAuthenticationScheme scheme = XAuthenticationScheme.XToken + ) { + // + // Validate Args ... + if (tokenName.IsNullOrEmpty ()) { + return null; + } + + // + var authService = context.GetAuthenticationService (); + if (authService.IsNull ()) { + return null; + } + + // + var authResult = await authService + .AuthenticateAsync ( + context, + scheme + .GetStringValue () + ); + + // + var result = authResult?.Properties? + .GetTokenValue (tokenName); + return result; + } + + /// + /// Retrieve Access Token + /// + /// + public static async Task GetAccessToken (this HttpContext context) { + // + var accessToken = context.Request.Headers[XAuthorization.Header] + .ToString (); + if (accessToken.IsNullOrEmpty ()) { + accessToken = await context + .GetTokenAsync (XAuthorization.AccessToken); + } + + // + if (accessToken + .ToNormalString () + .Contains (XAuthorization.TokenIdentifier + .ToNormalString ())) { + accessToken = accessToken + .Remove (0, XAuthorization.TokenIdentifier.Length); + } + + // + return accessToken; + } + + /// + /// Retrieve Refresh Token + /// + /// + public static async Task GetRefreshToken (this HttpContext context) { + // + var refreshToken = context.Request.Headers[XAuthorization.RefreshToken] + .ToString (); + if (refreshToken.IsNullOrEmpty ()) { + refreshToken = await context + .GetTokenAsync (XAuthorization.RefreshToken); + } + + // + return refreshToken; + } + + /// + /// Retrieve Access Token Expiration Date + /// + /// + public static async Task GetTokenExpiresAt (this HttpContext context) { + // + var expiresAtStr = context.Request.Headers[XAuthorization.ExpiresAt] + .ToString (); + if (expiresAtStr.IsNullOrEmpty ()) { + expiresAtStr = await context + .GetTokenAsync (XAuthorization.ExpiresAt); + } + + // + var expiresAt = expiresAtStr.ConvertTo (); + + // + return expiresAt; + } + + /// + /// Retrieve All Required Tokens + /// + /// + public static async Task RetrieveTokensAsXTokenResponse (HttpContext context) { + // + var accessToken = await GetAccessToken (context); + var refreshToken = await GetRefreshToken (context); + var expiresAt = await GetTokenExpiresAt (context); + + // + return new XTokenResponse { + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresAt = expiresAt + }; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Extensions\StreamExtensions.cs + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using xCommons.Extensions; + +namespace xIdentityService.Extensions +{ + public static partial class StreamExtensions + { + /// + /// Converts an Stream to Async Enumerable ... + /// + /// + /// + /// + /// + public static async IAsyncEnumerable ToAsyncEnumerable( + this Stream stream, + int bufferSize = 8192, + [System.Runtime.CompilerServices.EnumeratorCancellation] + CancellationToken cancellationToken = default + ) + { + // + var buffer = new byte[bufferSize]; + while (true) + { + // + int read = await stream + .ReadAsync( + buffer, + 0, + buffer.Length, + cancellationToken + ); + + // + if (read == 0) + { + yield break; + } + + // + yield return buffer.Take(read).ToArray(); + } + } + + /// + /// Converts an Stream to Async Enumerable ... + /// + /// + /// + /// + /// + public static async IAsyncEnumerable ToAsyncEnumerable( + this Stream stream, + int bufferSize = 8192, + [System.Runtime.CompilerServices.EnumeratorCancellation] + CancellationToken cancellationToken = default + ) + { + // + var buffer = new byte[bufferSize]; + while (true) + { + // + int read = await stream + .ReadAsync( + buffer, + 0, + buffer.Length, + cancellationToken + ); + if (read == 0) + { + yield break; + } + + // + T chunk = default(T); + var readedBuffer = buffer.Take(read).ToArray(); + var str = readedBuffer.FromBytes(); + if (str.IsNullOrEmpty()) + { + yield return chunk; + } + try + { + chunk = str.FromJSON(); + } + catch + { } + + // + yield return chunk; + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Extensions\XModelExtensions.cs + +using xIdentityModels.Dtos; +using xModels.Dtos; + +namespace xIdentityService.Extensions +{ + public static class XModelExtensions + { + public static XPersonDto ToXPersonDto(this XOpenActionUserInfoDto source) + { + // + var result = new XPersonDto + { + Avatar = source.Avatar, + Lastname = source.Lastname, + Username = source.Username, + Firstname = source.Firstname, + }; + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Extensions\XTokenExtensions.cs + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using IdentityModel.Client; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using xCommons.Constants; +using xCommons.Extensions; +using xExceptions.Constants; +using xExceptions.Models; +using xIdentityModels.Models; +using xIdentityService.Interfaces; + +public static partial class XTokenExtensions { + /// + /// Retrieve Tokens From Headers + /// + /// + /// + public static XLoginResponse RetrieveTokens (this HttpRequest source) { + // + var accessToken = source.Headers[XAuthorization.Header].ToString (); + var refreshToken = source.Headers[XAuthorization.RefreshToken].ToString (); + + // + var expiresAtStr = source.Headers[XAuthorization.ExpiresAt].ToString (); + long expiresAt = expiresAtStr.ConvertTo (); + + // + if (accessToken + .ToNormalString () + .Contains (XAuthorization.TokenIdentifier.ToNormalString ())) { + accessToken = accessToken.Remove (0, XAuthorization.TokenIdentifier.Length); + } + + // + return new XLoginResponse { + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresAt = expiresAt + }; + } + + /// + /// Retrieve Tokens From HttpContext + /// + /// + /// + public static async Task RetrieveTokens (this HttpContext source) { + // + var accessToken = await source.GetTokenAsync (XAuthorization.AccessToken); + if (accessToken + .ToNormalString () + .Contains (XAuthorization.TokenIdentifier.ToNormalString ())) { + accessToken = accessToken.Remove (0, XAuthorization.TokenIdentifier.Length); + } + + var refreshToken = await source.GetTokenAsync (XAuthorization.RefreshToken); + var expiresAtStr = await source.GetTokenAsync (XAuthorization.ExpiresAt); + long expiresAt = expiresAtStr.ConvertTo (); + + // + var headerRequestTokens = source.Request.RetrieveTokens (); + + // + var result = new XLoginResponse { + AccessToken = accessToken ?? headerRequestTokens.AccessToken, + RefreshToken = refreshToken ?? headerRequestTokens.RefreshToken, + ExpiresAt = expiresAt != 0 ? expiresAt : headerRequestTokens.ExpiresAt + }; + + // + return result; + } + + /// + /// Retrieve Tokens From ActionExecutingContext + /// + /// + /// + public static async Task RetrieveTokens (this ActionExecutingContext source) { + // + var result = await source.HttpContext.RetrieveTokens (); + return result; + } + + /// + /// Retrieve Tokens From XUserInfo instance + /// + /// + /// + public static XTokenResponse RetrieveTokens (this XUserClaimsInfoDto source) { + // + var result = new XLoginResponse { + AccessToken = source.AccessToken, + RefreshToken = source.RefreshToken, + ExpiresAt = source.ExpiresAt + }; + + // + return result; + } + + /// + /// Retrieve Tokens From IHeader Dictionary + /// + /// + /// + public static XLoginResponse RetrieveTokens (this IHeaderDictionary source) { + // + var accessToken = source[XAuthorization.Header].ToString () ?? ""; + if (accessToken + .ToNormalString () + .Contains (XAuthorization.TokenIdentifier.ToNormalString ())) { + accessToken = accessToken.Remove (0, XAuthorization.TokenIdentifier.Length); + } + + // + var refreshToken = source[XAuthorization.RefreshToken].ToString () ?? ""; + + // + var expiresAtStr = source[XAuthorization.ExpiresAt].ToString () ?? ""; + var expiresAt = expiresAtStr.ConvertTo (); + + // + var result = new XLoginResponse { + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresAt = expiresAt + }; + + // + return result; + } + + /// + /// Retrieve IXIdentityProvider from HttpContext + /// + /// + /// + public static IXIdentityProvider GetXApiIdentityProvider (this FilterContext source) { + // + var services = source.HttpContext.RequestServices; + return (IXIdentityProvider) services + .GetService (typeof (IXIdentityProvider)); + } + + /// + /// Generate XLoginResponse instance based on TokenResponse + /// + /// + /// + public static XLoginResponse CreateXLoginResponse (this TokenResponse source) { + // + if (source.IsNull () || + source.IsError) { + XException.InvalidToken.Throw (); + } + + // + var expiresAt = new DateTimeOffset (DateTime.UtcNow) + .ToUnixTimeSeconds () + + source.ExpiresIn; + + // + var result = new XLoginResponse { + AccessToken = source.AccessToken, + RefreshToken = source.RefreshToken, + ExpiresAt = expiresAt + }; + + // + return result; + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific IHeaderDictionary instance + /// + /// + /// + public static void SetXAuthenticationTokens (this IHeaderDictionary source, XLoginResponse model, string secret = null) { + // + source.Remove (XAuthorization.Header); + source.Remove (XAuthorization.RefreshToken); + source.Remove (XAuthorization.ExpiresAt); + source.Remove (XAuthorization.RevisionChecksum); + + // + source.Add (XAuthorization.Header, $"{XAuthorization.TokenIdentifier}{model.AccessToken}"); + source.Add (XAuthorization.RefreshToken, $"{model.RefreshToken}"); + source.Add (XAuthorization.ExpiresAt, $"{model.ExpiresAt}"); + + // + if (!secret.IsNullOrEmpty ()) { + var revisionChecksum = model.GetRevisionChecksum (secret); + source.Add (XAuthorization.RevisionChecksum, revisionChecksum); + } + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific IHeaderDictionary instance + /// + /// + /// + public static void SetXAuthenticationTokens (this HttpRequestHeaders source, XLoginResponse model) { + // + source.Remove (XAuthorization.Header); + source.Remove (XAuthorization.RefreshToken); + source.Remove (XAuthorization.ExpiresAt); + source.Remove (XAuthorization.RevisionChecksum); + + // + source.Add (XAuthorization.Header, $"{XAuthorization.TokenIdentifier}{model.AccessToken}"); + source.Add (XAuthorization.RefreshToken, $"{model.RefreshToken}"); + source.Add (XAuthorization.ExpiresAt, $"{model.ExpiresAt}"); + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific HttpRequest instance + /// + /// + /// + public static void SetXAuthenticationTokens (this HttpRequest source, XLoginResponse model) { + source.Headers.SetXAuthenticationTokens (model); + } + + /// + /// Add Authorization Tokens in Headers of + /// Specific HttpRequest instance + /// + /// + /// + public static void SetXAuthenticationTokens (this HttpRequestMessage source, XLoginResponse model) { + source.Headers.SetXAuthenticationTokens (model); + } + + /// + /// Add ReNewed Authorization Tokens in Headers of + /// Specific Response. + /// Later in HttpInterceptors we can Look for AccessToken-ReNewed + /// header Value and Renew User Tokens based on it + /// + /// + /// + public static void SetXAuthenticationTokens (this HttpResponse source, XLoginResponse model, string secret) { + source.Headers.SetXAuthenticationTokens (model, secret); + } + + /// + /// Determines a Token Can Refrsh or not + /// + /// + /// + public static bool IsRefreshable (this XLoginResponse source) { + // + var containsTokenData = !source.IsNull () && + !source.AccessToken.IsNullOrEmpty () && + !source.RefreshToken.IsNullOrEmpty (); + if (!containsTokenData) { + return false; + } + + // + var current = new DateTimeOffset (DateTime.UtcNow) + .ToUnixTimeSeconds (); + + // + // var result = current > source.ExpiresAt || current > source.ExpiresAt - XAuthorization.ThresholdBeforeTokenExpiration; + var result = current > source.ExpiresAt - XAuthorization.ThresholdBeforeTokenExpiration; + + // + return result; + } + + /// + /// Generate a Revision Checksum for new Tokens + /// + /// + /// + /// + public static string GetRevisionChecksum (this XLoginResponse source, string secret) { + return (source.AccessToken + + secret + + source.RefreshToken + + secret + + source.ExpiresAt.ToString () + ) + .ToMd5String (); + } + + /// + /// Generate a Revision Checksum for new Tokens + /// + /// + /// + /// + public static string GetRevisionChecksum (this XTokenResponse source, string secret) { + return (source.AccessToken + + secret + + source.RefreshToken + + secret + + source.ExpiresAt.ToString () + ) + .ToMd5String (); + } + + /// + /// Check an XLoginResponse instance is Valid with it's revision + /// + /// + /// + /// + /// + public static bool ValidateRevisionChecksum (this XLoginResponse source, string checksum, string secret) { + // + var xChecksum = source.GetRevisionChecksum (secret); + return checksum == xChecksum; + } + + /// + /// Check an XTokenResponse instance is Valid with it's revision + /// + /// + /// + /// + /// + public static bool ValidateRevisionChecksum (this XTokenResponse source, string checksum, string secret) { + // + var xChecksum = source.GetRevisionChecksum (secret); + return checksum == xChecksum; + } + + /// + /// Retrieve Exception Details if Fails + /// + /// + /// + public static Exception GetException (this TokenResponse source) { + // + // Validate Args ... + if (source.IsNull () || !source.IsError) { + return XException.ActionFailed.ToException (); + } + + // + XError error = source.ErrorDescription.FromJSON (); + return error.ToException (); + } + + /// + /// Convert XTokenResponse to Header Dictionary + /// + /// + /// + public static IDictionary ToHeaderDictionary (this XTokenResponse source) { + // + var result = new Dictionary { { XAuthorization.RefreshToken, source.RefreshToken }, + { XAuthorization.ExpiresAt, source.ExpiresAt.ToString () } + }; + + // + return result; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Extensions\XUserClaimsInfoDtoExtensions.cs + +using xCommons.Extensions; +using xIdentityModels.Constants; +using xIdentityModels.Models; + +namespace xIdentityService.Extensions { + public static class XUserClaimsInfoDtoExtensions { + public static string GetUserSelectByParam ( + this XUserClaimsInfoDto source, + XUserSelectBy userSelectBy = XUserSelectBy.Username + ) { + // + var result = ""; + + // + switch (userSelectBy) { + // + case XUserSelectBy.ID: + result = source.UserId; + break; + + // + case XUserSelectBy.Username: + result = source.UserName; + break; + + // + case XUserSelectBy.Email: + result = source.Email; + break; + + // + case XUserSelectBy.MobileNumber: + result = source.PhoneNumber; + break; + } + + // + if (result.IsNullOrEmpty ()) { + // + if (!source.UserId.IsNullOrEmpty ()) { + result = source.UserId; + } else if (!source.UserName.IsNullOrEmpty ()) { + result = source.UserName; + } else if (!source.Email.IsNullOrEmpty ()) { + result = source.Email; + } else if (!source.PhoneNumber.IsNullOrEmpty ()) { + result = source.PhoneNumber; + } + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Interfaces\IXBaseIdentityHttpProvider.cs + +using System; +using System.IO; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RestSharp; +using xCommons.Providers; +using xHttpService.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Models; +using xIdentityService.Models; + +namespace xIdentityService.Interfaces +{ + /// + /// an interface for Identity Provided Http Based Services ... + /// + public interface IXBaseIdentityHttpProvider + { + /// + /// Client Base Url ... + /// + /// + string BaseUrl { get; } + + /// + /// Logger ... + /// + /// + ILogger Logger { get; } + + /// + /// Powered Value ... + /// + /// + string XPoweredValue { get; } + + /// + /// Default Client Timeout ... + /// + /// + int DefaultClientTimeout { get; } + + /// + /// Default Buffering Chunk Size ... + /// + /// + int DefaultBufferingChunckSize { get; } + + /// + /// Validation Provider ... + /// + /// + XValidationProvider ValidationProvider { get; } + + /// + /// Get an Instance of Http Client + /// + /// + HttpClient GetHttpClient(XTokenResponse tokens = null); + HttpClient GetHttpClient(XUserClaimsInfoDto userInfo = null); + + /// + /// Get an Instance of RestClient + /// + /// + RestClient GetRestClient(XTokenResponse tokens = null); + RestClient GetRestClient(XUserClaimsInfoDto userInfo = null); + + /// + /// Get an Instance of RestRequest + /// + /// + /// + /// + /// + /// + /// + RestRequest GetRestRequet( + Enum endpoint, + bool addXPoweredValue = true, + IDictionary @params = null, + IDictionary headers = null, + IDictionary queryStrings = null + ); + + /// + /// Handle Call Specific Request and retrieve Response + /// + /// + /// + /// + /// + /// + /// + /// + Task RunRestRequest( + RestClient client, + RestRequest request, + XHttpMethod method, + bool supportRefreshingTokens = true, + CancellationToken cancellationToken = default + ); + + /// + /// Access a Get Request Stream as AsyncEnumerable ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task StreamData( + Enum endpoint, + int bufferLength = 8194, + bool addXPoweredValue = true, + XTokenResponse tokens = null, + IDictionary @params = null, + IDictionary headers = null, + IDictionary queryStrings = null, + CancellationToken cancellationToken = default + ); + + /// + /// Handle Call Specified Request and Retrieve XBaseRestResponse ... + /// + /// + /// + /// + /// + /// + /// + /// an instance of XBaseRestResponse + Task> RunRestRequestByResponse( + RestClient client, + RestRequest request, + XHttpMethod method, + bool supportRefreshingTokens = true, + CancellationToken cancellationToken = default + ); + + /// + /// Get User SelectBy Param + /// + /// + /// + /// + /// + string GetUserSelectByParam( + XActionRequest model, + bool forceNotNull = true, + ICollection excludes = null + ); + + /// + /// Converts UserInfo to TokenResponse model ... + /// + /// + /// + XTokenResponse ToTokenResponse(XUserClaimsInfoDto userInfo = null); + + /// + /// Prepare Url Address of Specified Enum by Filling Params + /// and Query Strings Attach ... + /// + /// + /// + /// + /// + /// + string PrepareUrl( + Enum endpoint, + bool attachBaseUrl = false, + IDictionary @params = null, + IDictionary queryStrings = null + ); + + /// + /// Prepare Url Address by Filling Params + /// and Query Strings Attach ... + /// + /// + /// + /// + /// + /// + string PrepareUrl( + string url, + bool attachBaseUrl = false, + IDictionary @params = null, + IDictionary queryStrings = null + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Interfaces\IXBaseReferenced.cs + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xDataService.Models; +using xIdentityModels.Models; +using xModels.Base; + +namespace xIdentityService.Interfaces +{ + /// + /// a Referenced Service is a Service which can Referenced to other Items ... + /// + public interface IXBaseReferenced + where TEntity : XBaseEntity + { + /// + /// Get Reference Identifier for Specified Provider and Specified Item ... + /// + /// + /// + /// + string GetIdentifier( + string providedFor, + TReferenceKey forProvidedId + ); + + /// + /// Check Specified Item has Refernce to Provider ... + /// + /// + /// + /// + /// + /// + Task IsProvidedFor( + string providedFor, + TReferenceKey forProvidedId, + TKey id, + CancellationToken cancellationToken = default + ); + + /// + /// Add Specified Reference to Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task AddReference( + string providedFor, + TReferenceKey forProvidedId, + TKey id, + int forIndex = 0, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Specified Reference from Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task RemoveReference( + string providedFor, + TReferenceKey forProvidedId, + TKey id, + int forIndex = 0, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get Specified References ... + /// + /// + /// + /// + /// + /// + Task>> GetReferences( + string providedFor, + TKey id, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Interfaces\IXBaseReferencedController.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using xDataService.Models; +using xModels.Base; + +namespace xIdentityService.Interfaces +{ + public interface IXBaseReferencedController + where TEntity : XBaseEntity + { + /// + /// Check an Item Has Reference to Specified ProvideFor and Specified forProvidedId ... + /// + /// + /// + /// + /// + /// + [HttpGet("{id}/Reference/IsProvidedFor")] + Task> IsProvidedFor( + [FromRoute] TKey id, + [FromQuery] string providedFor, + [FromQuery] object forProvidedId, + CancellationToken cancellationToken = default + ); + + /// + /// Add a Reference to Item for Specified ProvideFor and Specified forProvidedId ... + /// + /// + /// + /// + /// + /// + /// + [HttpPost("{id}/Reference")] + Task AddReference( + [FromRoute] TKey id, + [FromQuery] string providedFor, + [FromQuery] object forProvidedId, + [FromQuery] int forIndex = 0, + CancellationToken cancellationToken = default + ); + + /// + /// Remove a Reference from Item for Specified ProvideFor and Specified forProvidedId ... + /// + /// + /// + /// + /// + /// + /// + [HttpDelete("{id}/Reference")] + Task RemoveReference( + [FromRoute] TKey id, + [FromQuery] string providedFor, + [FromQuery] object forProvidedId, + [FromQuery] int forIndex = 0, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve References of Specified Itemfor providedFor ... + /// + /// + /// + /// + /// + [HttpGet("{id}/References")] + Task>>> GetReferences( + [FromRoute] TKey id, + [FromQuery] string providedFor, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Interfaces\IXIdentityBaseReferencedProvider.cs + +using System.Collections.Generic; +using System.Threading.Tasks; +using xDataService.Models; +using xIdentityModels.Models; +using xModels.Dtos; + +namespace xIdentityService.Interfaces +{ + /// + /// in Some Cases we have Entities Which Can Referenced for Specified + /// Provider and Types ... + /// this interface Globaly introduce all available Actions for those ... + /// + /// TKey: type of Provider Models Keys ... + /// TModel: type of Dto which Referenced ... + /// + public interface IXIdentityBaseReferencedProvider + { + // + #region Props ... + /// + /// Provider Identifier ... + /// + string Provider { get; } + #endregion + + // + #region Referenced Actions ... + /// + /// Get Specified Indexed Reference for Specific Provided ID ... + /// + /// + /// + /// + Task GetReference( + TKey providedForId, + int forIndex = 0 + ); + + /// + /// Retrieve all Exists References of Specific Provided ID ... + /// + /// + /// + Task> GetAllReferences(TKey providedForId); + + /// + /// Extract all Referencing Models for Specified Key ... + /// + /// + /// + Task>> GetAllReferencings(TKey providedForId); + + /// + /// Count References ... + /// + /// + /// + Task CountReferences(TKey providedForId); + + /// + /// Retrieve References of Specific Provided ID as Query ... + /// + /// + /// + Task> QueryReferences( + XQuery query, + TKey providedForId + ); + + /// + /// Add Reference a Model to Specific Provided ID ... + /// + /// + /// + /// + /// + /// + Task AddReference( + TModel model, + TKey providedForId, + string connectionId = null, + XUserClaimsInfoDto userInfo = null + ); + + /// + /// Add Reference a Model to Specific XRefence ... + /// + /// + /// + /// + /// + /// + Task AddReference( + TModel model, + XReference reference, + string connectionId = null, + XUserClaimsInfoDto userInfo = null + ); + + /// + /// Remove Reference a Model for Specific Provided ID ... + /// + /// + /// + /// + /// + /// + Task RemoveReference( + TModel model, + TKey providedForId, + string connectionId = null, + XUserClaimsInfoDto userInfo = null + ); + + /// + /// Remove Reference a Model for Specific XRefence ... + /// + /// + /// + /// + /// + /// + Task RemoveReference( + TModel model, + XReference reference, + string connectionId = null, + XUserClaimsInfoDto userInfo = null + ); + + /// + /// Remove All References to Specified Key ... + /// + /// + /// + /// + /// + Task RemoveReferences( + TKey providedForId, + string connectionId = null, + XUserClaimsInfoDto userInfo = null + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Interfaces\IXIdentityProvider.cs + +using System.Collections.Generic; +using System.Threading.Tasks; +using IdentityModel.Client; +using Microsoft.AspNetCore.Http; +using RestSharp; +using xHttpService.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Dtos; +using xIdentityModels.Models; +using xIdentityModels.Navigations; +using xModels.Dtos; + +namespace xIdentityService.Interfaces +{ + public partial interface IXIdentityProvider + { + // + string BaseUrl { get; } + string RevisionSecretKey { get; } + IXSecurityProvider SecurityProvider { get; } + + // + #region Tools ... + string GetUserSelectByParam( + XActionRequest model, + bool forceNotNull = true, + ICollection excludes = null + ); + #endregion + + // + #region Tools ... + Task RunRestRequest( + RestClient client, + RestRequest request, + XHttpMethod method, + bool supportRefreshingTokens = false + ); + #endregion + + // + #region Authentication ... + Task RequestDiscoveryDocument(); + Task RequestScopeAccessToken(string scope); + Task Introspection(XTokenResponse model); + Task Login(XLoginRequest model); + Task Logout(XTokenResponse model); + Task Authenticate(XLoginRequest model); + Task RefreshTokens(XTokenResponse model); + + Task ChangePassword( + XTokenResponse tokens, + XActionRequest model + ); + #endregion + + // + #region Profile ... + Task> GetUserNames( + XTokenResponse tokens, + string userIds + ); + + Task> GetUserNameIds( + XTokenResponse tokens, + XUserNameIdRequest model + ); + + Task GetUserProfile( + XTokenResponse tokens, + string userSelectByParam + ); + + Task> QueryUsers( + XTokenResponse tokens, + XQuery query + ); + + Task> QueryInRoleUsers( + XTokenResponse tokens, + string role, + XQuery query, + bool forceRole = false + ); + + Task> QueryAvatars( + XTokenResponse tokens, + string userSelectByParam, + XQuery query + ); + + Task ProfileUpdateAsync( + XTokenResponse tokens, + string userSelectByParam, + XProfileUpdateRequest model + ); + + Task FullProfileUpdateAsync( + XTokenResponse tokens, + string userSelectByParam, + XProfileUpdateRequest request + ); + + Task AddAvatar( + XTokenResponse tokens, + IFormFile file + ); + + Task AddAvatars( + XTokenResponse tokens, + IFormFileCollection files + ); + + Task SetAvatar( + XTokenResponse tokens, + int avatarId + ); + + Task RemoveAvatars( + XTokenResponse tokens, + string avatarIds + ); + #endregion + + // + #region Search ... + /// + /// Query Open To Search User Profiles ... + /// + /// requested user's identifiers + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XUserProfileDto + Task> QueryOpenToSearchUsers( + XTokenResponse tokens, + XQuery query + ); + #endregion + + // + #region Registration ... + Task CanRegister(string userSelectByParam); + + Task InviteUser( + XTokenResponse tokens, + XActionRequest model + ); + + Task RequestRegistration( + XActionRequest model + ); + + Task AddAccountInfo( + XActionRequest model + ); + + Task AttachProfileImage( + string actionToken, + IFormFile file + ); + + Task FinishRegistration(XActionRequest model); + + Task IsConfirmedEmail( + XTokenResponse tokens, + string userSelectByParam + ); + + Task IsConfirmedMobile( + XTokenResponse tokens, + string userSelectByParam + ); + + Task RequestConfirmRegistration(XActionRequest model); + + Task ConfirmRegistration( + XActionRequest model + ); + + Task RequestConfirmMobile(XActionRequest model); + + Task ConfirmMobileNumber(XActionRequest model); + + Task RequestConfirmEmail(XActionRequest model); + + Task ConfirmEmailAddress(XActionRequest model); + + Task RequestResetPassword(XActionRequest model); + + Task ResetPassword(XActionRequest model); + #endregion + + // + #region Friendship ... + Task Follow( + XTokenResponse tokens, + string destUser + ); + + Task Cancel( + XTokenResponse tokens, + string destUser + ); + + Task UnFollowFollower( + XTokenResponse tokens, + string destUser + ); + + Task UnFollowFollowing( + XTokenResponse tokens, + string destUser + ); + + Task Block( + XTokenResponse tokens, + string destUser + ); + + Task UnBlock( + XTokenResponse tokens, + string destUser + ); + + Task AcceptRequest( + XTokenResponse tokens, + string destUser + ); + + Task RejectRequest( + XTokenResponse tokens, + string destUser + ); + + Task GetFriendshipInfo( + XTokenResponse tokens, + string destUser + ); + + Task IsFollower( + XTokenResponse tokens, + string destUser + ); + + Task GetFollower( + XTokenResponse tokens, + string destUser + ); + + Task GetFollowerState( + XTokenResponse tokens, + string destUser + ); + + Task> GetFollowers(XTokenResponse tokens); + + Task> GetAllFollowers(XTokenResponse tokens); + + Task> GetFollowingList(XTokenResponse tokens); + + Task> QueryFollowers( + XQuery query, + string destUser, + XTokenResponse tokens + ); + + Task IsFollowing( + XTokenResponse tokens, + string destUser + ); + + Task GetFollowing( + XTokenResponse tokens, + string destUser + ); + + Task GetFollowingState( + XTokenResponse tokens, + string destUser + ); + + Task> GetFollowings(XTokenResponse tokens); + + Task> GetAllFollowings(XTokenResponse tokens); + + Task> GetFollowersList(XTokenResponse tokens); + + Task> QueryFollowings( + XQuery query, + string destUser, + XTokenResponse tokens + ); + #endregion + + // + #region Admin ... + Task> Ban( + XTokenResponse tokens, + XUserNameIdRequest model + ); + + Task> UnBan( + XTokenResponse tokens, + XUserNameIdRequest model + ); + + Task IsBanned( + XTokenResponse tokens, + string userSelectByParam + ); + #endregion + + // + #region Open Actions ... + // + XDeviceDto GetDevice(); + + // + Task OpenGet( + XOpenActionInnerDto model + ); + + // + Task GetUserInfo( + XDeviceDto device, + string userSelectByParam + ); + Task GetUserInfo( + XOpenActionInnerDto dto + ); + Task GetUserInfo( + string token, + string request + ); + + // + Task> GetUserInfos( + XDeviceDto device, + string ids + ); + Task> GetUserInfos( + XOpenActionInnerDto dto + ); + Task> GetUserInfos( + string token, + string request + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Interfaces\IXProxyHelper.cs + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using xIdentityService.Configuration; + +namespace xIdentityService.Interfaces { + public interface IXProxyHelper { + /// + /// Proxy Configuration ... + /// + /// + XProxyConfiguration Configuration { get; } + + /// + /// retrie Normalize Host ... + /// + /// + string NormalizeHost (); + + /// + /// Validate XProxy Configuration for Client ... + /// + /// + bool ValidateConfiguration (); + + /// + /// Retrieve all Prepared url of Routes ... + /// + /// + IEnumerable GetRouteUrls (); + + /// + /// retrieve all registred Routes ... + /// + /// + IEnumerable GetRoutes (); + + /// + /// Prepare a request url for a Proxy Route ... + /// + /// + /// + /// + string PrepareActionUrlForXProxyRoute ( + string url, + XProxyRouteDescriptor descriptor + ); + + /// + /// Prepare a request url for a Selected Proxy Route ... + /// + /// + /// + /// + string PrepareActionUrlForXProxyRoute ( + string url, + Expression> whereClause + ); + + /// + /// Find an Specific Route ... + /// + /// + /// + XProxyRouteDescriptor FindRoute (Expression> whereClause); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Interfaces\IXSecurityProvider.cs + +namespace xIdentityService.Interfaces +{ + public partial interface IXSecurityProvider + { + string Encrypt(string plainText); + string Decrypt(string cipherText); + string EncryptFromBytes(byte[] textBytes); + string DecryptFromBytes(byte[] cipherBytes); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Interfaces\IXTokenProvider.cs + +using System.Threading.Tasks; +using xIdentityModels.Models; + +namespace xIdentityService.Interfaces { + public partial interface IXTokenProvider { + Task IsTokenExists (string accessToken); + Task IsTokenExists (XTokenResponse tokens); + Task RemoveToken (string accessToken); + Task RemoveToken (XTokenResponse tokens); + Task RetrieveToken (string accessToken); + Task AddToken (XTokenResponse tokens); + Task UpdateToken (XTokenResponse tokens); + Task AddOrUpdateToken (XTokenResponse tokens); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Middlewares\XIdentityTokenRefresher.cs + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using xCommons.Constants; +using xCommons.Extensions; +using xIdentityModels.Extensions; +using xIdentityService.Interfaces; + +namespace xIdentityService.Middlewares { + public partial class XIdentityTokenRefresherMiddleware { + // + #region Properties ... + private readonly ILogger logger; + private readonly RequestDelegate next; + private readonly IXTokenProvider tokenProvider; + private readonly IXIdentityProvider identityProvider; + #endregion + + // + #region Constructor ... + public XIdentityTokenRefresherMiddleware ( + RequestDelegate next, + ILoggerFactory loggerFactory, + IXTokenProvider tokenProvider, + IXIdentityProvider identityProvider + ) { + // + this.next = next; + this.tokenProvider = tokenProvider; + this.identityProvider = identityProvider; + this.logger = loggerFactory + .CreateLogger (); + } + #endregion + + // + #region Actions ... + public async Task Invoke (HttpContext context) { + // + // Handle Request ... + await HandleRequest (context); + + // + // Do Main Task ... + try { + await next.Invoke (context); + } catch (Exception ex) { + logger.LogError ($"Error: {ex.Message}"); + } + + // + // Handle Response ... + await HandleResponse (context); + } + #endregion + + // + #region Private ... + private async Task HandleRequest (HttpContext context) { + // + // Log ... + logger.LogInformation ($"Start Process Request ..."); + + // + // Check Access Token ... + var hasAccessToken = HasAccessToken (context); + if (!hasAccessToken) { + return; + } + + // + // Check Access Token ... + var accessToken = GetAccessToken (context); + var isExists = await tokenProvider + .IsTokenExists (accessToken); + if (!isExists) { + return; + } + + // + // Try Check is Refreshable ... + var tokens = await tokenProvider + .RetrieveToken (accessToken); + if (tokens.IsNull ()) { + return; + } + + // + // Check is Expired or Refreshable ... + var isRefreshable = tokens.IsRefreshable (); + if (!isRefreshable) { + return; + } + + // + // Refresh Token ... + var refreshedTokens = await identityProvider + .RefreshTokens (tokens); + if (refreshedTokens.IsNull ()) { + return; + } + + // + // Set Refreshed AccessToken to Request ... + context + .Request + .Headers[XAuthorization.Header] = + $"{XAuthorization.TokenIdentifier}{refreshedTokens.AccessToken}"; + } + + private async Task HandleResponse (HttpContext context) { + // + // Log ... + logger.LogInformation ($"Start Process Response ..."); + + // + await Task.Run (() => { }); + } + + private bool HasAccessToken (HttpContext context) { + // + // Check Headers ... + if (context.IsNull () || + context.Request.IsNull () || + context.Request.Headers.IsNull ()) { + return false; + } + + // + var result = context + .Request + .Headers + .ContainsKey ( + XAuthorization.Header + ); + + // + return result; + } + + private string GetAccessToken (HttpContext context) { + // + var hasAccessToken = HasAccessToken (context); + if (!hasAccessToken) { + return string.Empty; + } + + // + // Get Result ... + var result = context + .Request + .Headers[ + XAuthorization.Header + ]; + result = result + .ToString () + .Replace ( + XAuthorization.TokenIdentifier, + "" + ); + + // + return result; + } + #endregion + } + + /// + /// Provide all DI Requirements for XIdentityTokenRefresher Middleware + /// + public static class XIdentityTokenRefresherDIHelper { + /// + /// Use XTokenValidator Middleware + /// + /// + public static void UseXIdentityTokenRefresher (this IApplicationBuilder app) { + app.UseMiddleware (); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Middlewares\XProxyMiddleware.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using IdentityModel; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityService.Configuration; +using xIdentityService.Constants; +using xIdentityService.Interfaces; +using xIdentityService.Providers; +using xModels.Base; + +namespace xIdentityService.Middlewares { + /// + /// Proxy Server Middleware ... + /// + public class XProxyMiddleware : XBaseClass { + // + #region Pros ... + private readonly HttpClient HTTP_CLIENT; + private readonly RequestDelegate NEXT_MIDDLEWARE; + private readonly XProxyConfiguration CONFIGURATION; + #endregion + + // + #region Constructor ... + public XProxyMiddleware ( + ILoggerFactory loggerFactory, + RequestDelegate nextMiddleware, + XProxyConfiguration configuration, + IHttpClientFactory httpClientFactory + ) : base (loggerFactory) { + // + this.CONFIGURATION = configuration; + this.NEXT_MIDDLEWARE = nextMiddleware; + + // + // Create Http Client ... + this.HTTP_CLIENT = httpClientFactory + .CreateClient (XProxyConstants.XPROXY_HTTP_CLIENT); + + // + // Logging Server Configuration ... + LogXProxyServerConfig (); + } + #endregion + + // + #region Public ... + public async Task InvokeAsync (HttpContext context) { + // + #region Bussiness Logic ... + // + var routeDescriptor = GetRouteDescriptor (context.Request); + if (!routeDescriptor.IsNull ()) { + // + #region Handle Authorization ... + // + try { + // + // Handle XPowered ... + if (routeDescriptor.EnableXPoweredByAthorization) { + // + var isPoweredByPass = context.Request.Headers + .Any (h => h.Key + .ToNormalString () == xCommons.Constants.XAuthorization.XPoweredBy + .ToNormalString () && + h.Value + .ToString () + .ToNormalString () == CONFIGURATION.XPoweredValue + .ToNormalString () + ); + + // + if (!isPoweredByPass) { + ThrowNotAuthorizedException (); + } + } + + // + // Handle EndableAuthorization ... + if (routeDescriptor.EnableAuthorization) { + // + // Retrieve User Info for Authentication ... + var userName = context.User.Identity.Name; + var isUserAuthenticated = !userName.IsNull () && + context.User.Identity.IsAuthenticated; + if (!isUserAuthenticated && + !routeDescriptor.AllowedScopes.HasChild () + ) { + ThrowNotAuthorizedException (); + } + + // + // Check Allowed Scopes Authorization ... + if (routeDescriptor.AllowedScopes.HasChild ()) { + // + var scopeClaims = context.User.Claims + .Where (c => c.Type == JwtClaimTypes.Scope) + .Select (c => c.Value); + var isScopePassed = scopeClaims.HasChild () && + scopeClaims.Any (c => routeDescriptor.AllowedScopes + .Any (allowedScope => allowedScope + .ToNormalString () == c + .ToNormalString () + ) + ); + if (!isScopePassed) { + ThrowNotAuthorizedException (); + } + } + + // + // Check Allowed Roles Authorization ... + if ( + isUserAuthenticated && + routeDescriptor.AllowedRoles.HasChild () + ) { + // + var roleClaim = context.User.Claims + .FirstOrDefault (c => c.Type == JwtClaimTypes.Role); + var isRolePassed = !roleClaim.IsNull () && + routeDescriptor.AllowedRoles + .Any (role => role + .ToNormalString () == roleClaim.Value + .ToNormalString ()); + if (!isRolePassed) { + ThrowNotAuthorizedException (); + } + } + } + } catch { + context.Response.StatusCode = (int) HttpStatusCode.Unauthorized; + return; + } + #endregion + + // + // Extract Request Uri from Received Request ... + var requestUri = GetUri (context.Request); + if (!requestUri.IsNull ()) { + // + // Retrieve Request based on current ... + var request = GetRequestMessage ( + uri: requestUri, + context: context + ); + + // + SendLog ($"Send Request to Recieve Response ...", LogLevel.Information); + + // + // Send Request and Retrieve Response ... + try { + // + var response = await HTTP_CLIENT.SendAsync ( + request, + HttpCompletionOption.ResponseContentRead + ); + + // + // Set Current Response Status Code ... + context.Response.StatusCode = (int) response.StatusCode; + + // + // Retrieve Response Headers and Set To Current Response ... + HandleResponseHeaders (context, response); + + // + // Processing Response Content ... + await ProcessResponseContent (context, response); + } catch (Exception ex) { + // + LogMessage ($"Exception: {ex.Message}", LogLevel.Error); + + // + context.Response.StatusCode = (int) HttpStatusCode.BadRequest; + } + + // + return; + } + } + #endregion + + // + await NEXT_MIDDLEWARE (context); + } + #endregion + + // + #region Private ... + /// + /// Throw Required Exception ... + /// + private void ThrowNotAuthorizedException () { + throw XException.NotAuthorized.ToException (); + } + + /// + /// Show propper logs if Enabled ... + /// + /// + /// + private void SendLog (string message, LogLevel logLevel = LogLevel.Information) { + // + if (!CONFIGURATION.EnableLogging) { + return; + } + + // + LogMessage ( + message: message, + logLevel: logLevel + ); + } + + /// + /// Log Proxy Server Configuration ... + /// + private void LogXProxyServerConfig () { + // + // Retrieve XProxyConfiguration class from Configurations and Validate them ... + if (CONFIGURATION.IsNull ()) { + throw XException.InvalidConfiguration.ToException (); + } + + // + // Try to Log Proxy Configuration ... + ConsoleMessage ("==============================="); + ConsoleMessage ("= Proxy Server Configurations: "); + ConsoleMessage ("==============================="); + + // + ConsoleMessage ($"Host: {CONFIGURATION.Host}"); + ConsoleMessage ($"XPoweredValue: {CONFIGURATION.XPoweredValue}"); + ConsoleMessage ($"EnableLogging: {CONFIGURATION.EnableLogging}"); + ConsoleMessage ($"DisableSSLCheck: {CONFIGURATION.DisableSSLCheck}"); + ConsoleMessage ($"AllowAutoRedirect: {CONFIGURATION.AllowAutoRedirect}"); + ConsoleMessage ($" "); + ConsoleMessage ($"Routes: "); + ConsoleMessage ($" "); + + // + CONFIGURATION.Routes + .ToList () + .ForEach (routeDescriptor => { + ConsoleMessage ("==============================="); + ConsoleMessage ($"= Rote => {routeDescriptor.Route}"); + ConsoleMessage ("==============================="); + ConsoleMessage ($"AllowedRoles: {routeDescriptor.AllowedRoles.ToJSON()}"); + ConsoleMessage ($"AllowedScopes: {routeDescriptor.AllowedScopes.ToJSON()}"); + ConsoleMessage ($"EnableAuthorization: {routeDescriptor.EnableAuthorization}"); + ConsoleMessage ($"EnableXPoweredByAthorization: {routeDescriptor.EnableXPoweredByAthorization}"); + ConsoleMessage ($" "); + }); + } + + /// + /// Retrieve Destination Request Path based on Current Request ... + /// + /// + /// + private Uri GetUri (HttpRequest request) { + // + // Create temp result ... + Uri result = null; + var requestPath = ""; + + // + SendLog ($"Receive Request: {request.Path} ...", LogLevel.Information); + + // + // Try to Findout Route Descriptor ... + var routeDescriptor = GetRouteDescriptor (request); + + // + // Route Descriptor Exists && Path for Delegating ... + if (!routeDescriptor.IsNull () && + request.Path + .StartsWithSegments (routeDescriptor.Route) + ) { + // + // Prepare Request Path by Cleaning Starter Path Segment ... + requestPath = request.Path + .ToString () + .Replace (routeDescriptor.Route, ""); + if (requestPath.StartsWith ("/")) { + requestPath = requestPath.Substring (1); + } + + // + // Decoding URL ... + requestPath = HttpUtility.UrlDecode (requestPath); + + // + var colonIndex = requestPath.IndexOf (":"); + if (colonIndex > -1) { + // + var doubleSlash = requestPath.Substring (colonIndex + 1, 2); + var isDoubleSlash = doubleSlash == "//"; + + // + if (!isDoubleSlash) { + // + var protocol = requestPath + .Substring (0, colonIndex); + var section = requestPath + .Substring (colonIndex + 2, requestPath.Length - colonIndex - 2); + + // + requestPath = $"{protocol}://{section}"; + } + } + } + + // + // Validate Request Path ... + if (!requestPath.IsNullOrEmpty ()) { + // + result = new Uri (requestPath); + + // + SendLog ($"Process Request: {requestPath} ...", LogLevel.Information); + } + + // + return result; + } + + /// + /// Retrieve Propper Request Message based on HttpContext and uri ...F + /// + /// + /// + /// + private HttpRequestMessage GetRequestMessage ( + HttpContext context, + Uri uri + ) { + // + var result = new HttpRequestMessage (); + FillRequest (context, result); + + // + // Handle Queries ... + SendLog ($"Start Processing Queries: "); + foreach (var query in context.Request.Query) { + // + var key = query.Key; + var value = HttpUtility.UrlDecode (query.Value); + + // + uri = new Uri ( + QueryHelpers.AddQueryString ( + uri.OriginalString, + new Dictionary { + [key] = value + } + ) + ); + + // + SendLog ($"Query : {key}:{value} was Processed ..."); + } + + // + result.RequestUri = uri; + result.Headers.Host = uri.Host; + + // + // Retrieve Request Method ... + result.Method = GetMethod (context.Request.Method); + + // + return result; + } + + /// + /// Fill Request Message by provide HttpContext ... + /// + /// + /// + private void FillRequest ( + HttpContext context, + HttpRequestMessage message + ) { + // + var requestMethod = context.Request.Method; + + // + if (!HttpMethods.IsGet (requestMethod) && + !HttpMethods.IsHead (requestMethod) && + !HttpMethods.IsTrace (requestMethod) && + !HttpMethods.IsDelete (requestMethod) + ) { + // + var streamContent = new StreamContent (context.Request.Body); + message.Content = streamContent; + } + + // + // Handle Base Headers ... + var regularHeaders = context.Request.Headers + .Where (hr => hr.Key != XProxyConstants.XPROXY_ACCEPTED_HEADERS && + !hr.Key + .Contains ( + XProxyConstants.XPROXY_FORWARD_HEADER + ) + ); + + // + // Handle Aceepted Headers ... + IEnumerable acceptedHeaders = null; + var acceptedHeadersKeyValue = context.Request.Headers + .FirstOrDefault (hr => + hr.Key == XProxyConstants.XPROXY_ACCEPTED_HEADERS + ); + if (!acceptedHeadersKeyValue.IsNull ()) { + // + acceptedHeaders = acceptedHeadersKeyValue.Value + .ToString () + .ParseListString (); + + // + // Filter Regular Headers ... + if ( + regularHeaders.HasChild () && + acceptedHeaders.HasChild () + ) { + // + regularHeaders = regularHeaders + .Where (rhr => acceptedHeaders + .Contains (rhr.Key) + ); + } + } + foreach (var header in regularHeaders) { + // + SendLog ($"Adding Header: {header.Key}:{header.Value} ..."); + + // + message.Headers.Add ( + header.Key, + header.Value + .ToString () + ); + } + + // + // Handle ForWarded Headers ... + // Since Forward Headers is Custom kind, this means user actually need this + // Header, so there is no need to put them on AcceptedHeaders ... + var forwardHeaders = context.Request.Headers + .Where (hr => hr.Key + .Contains ( + XProxyConstants.XPROXY_FORWARD_HEADER + ) + ); + foreach (var header in forwardHeaders) { + // + var key = header.Key + .Replace ( + XProxyConstants.XPROXY_FORWARD_HEADER, + string.Empty + ); + + // + SendLog ($"Forwarding Header: {header.Key} to {key} ..."); + + // + message.Headers + .Add ( + key, + header.Value + .ToString () + ); + } + } + + /// + /// Retreive and Extract Method Type of HttpRequest ... + /// + /// + /// + private static HttpMethod GetMethod (string method) { + // + if (HttpMethods.IsDelete (method)) { + return HttpMethod.Delete; + } else if (HttpMethods.IsGet (method)) { + return HttpMethod.Get; + } else if (HttpMethods.IsHead (method)) { + return HttpMethod.Head; + } else if (HttpMethods.IsOptions (method)) { + return HttpMethod.Options; + } else if (HttpMethods.IsPost (method)) { + return HttpMethod.Post; + } else if (HttpMethods.IsPut (method)) { + return HttpMethod.Put; + } else if (HttpMethods.IsTrace (method)) { + return HttpMethod.Trace; + } + + // + return new HttpMethod (method); + } + + /// + /// add all headers exists in HttpResponse MEssage to Current Context Response Headers ... + /// + /// + /// + private void HandleResponseHeaders ( + HttpContext context, + HttpResponseMessage message + ) { + // + // Handle Message Headers ... + foreach (var header in message.Headers) { + context.Response.Headers[header.Key] = header.Value + .ToArray (); + } + + // + // Handle Message Content Headers ... + foreach (var header in message.Content.Headers) { + context.Response.Headers[header.Key] = header.Value + .ToArray (); + } + + // + context.Response.Headers.Remove ("transfer-encoding"); + } + + /// + /// Processing Response Content and Handle it on HttpContextResponse ... + /// + /// + /// + /// + private async Task ProcessResponseContent ( + HttpContext context, + HttpResponseMessage message + ) { + // + // Reading Content as Byte Array ... + var contentBytes = await message.Content + .ReadAsByteArrayAsync (); + + // + // Check ContentType of Response ... + if ( + IsContentOfType (message, XResponseContentTypes.HTML) || + IsContentOfType (message, XResponseContentTypes.JSON) || + IsContentOfType (message, XResponseContentTypes.JavaScript) + ) { + // + // If their String Content ... + var stringContent = Encoding.UTF8.GetString (contentBytes); + + // + // TODO: here we can change String Contents ... + + // + // Writing new Content to Response ... + await context.Response + .WriteAsync ( + stringContent, + Encoding.UTF8 + ); + } else { + // + // if they have non string content ... + await context.Response.Body + .WriteAsync ( + buffer: contentBytes, + offset: 0, + count: contentBytes.Length + ); + } + + // + SendLog ($"Response Content Processed ...", LogLevel.Information); + } + + /// + /// Check Content Type of Specific Response ... + /// + /// + /// + /// + private bool IsContentOfType ( + HttpResponseMessage response, + string type + ) { + // + // Create EMpty Result ... + var result = false; + + // + // Retrieve and Check Content Type of Response ... + if (response.Content?.Headers?.ContentType != null) { + result = response.Content.Headers.ContentType.MediaType == type; + } + + // + return result; + } + + /// + /// Retrieve and Extract Route Descriptor ... + /// + /// + /// + private XProxyRouteDescriptor GetRouteDescriptor (HttpRequest request) { + // + // Validate Args ... + if ( + CONFIGURATION.IsNull () || + !CONFIGURATION.Routes.HasChild () + ) { + return null; + } + + // + var result = CONFIGURATION.Routes + .FirstOrDefault (rd => !rd.Route.IsNullOrEmpty () && + request.Path + .StartsWithSegments (rd.Route + .ToNormalString () + ) + ); + + // + return result; + } + #endregion + } + + /// + /// Provide all requirements for Dependency Injection Handler ... + /// + public static class XDIHelperExtension { + /// + /// Retrieve XProxy Middelware Configuration from Configuration ... + /// + /// + /// + public static XProxyConfiguration GetXProxyConfiguration (this IConfiguration config) { + // + var xProxyConfiguration = config.GetSection (ConfigurationNodeNames.XPROXY_CONFIGURATION_NODE_NAME); + return xProxyConfiguration.Get (); + } + + /// + /// Register HTTP Handler for XProxy Server ... + /// + /// + /// + /// + public static void AddXProxyHttpHandler ( + this IServiceCollection services, + IConfiguration configuration, + HttpClientHandler httpHandler = null + ) { + // + var proxyConfig = configuration.GetXProxyConfiguration (); + if (proxyConfig.IsNull ()) { + throw XException.InvalidConfiguration.ToException (); + } + + // + services.AddXProxyHttpHandler ( + httpHandler: httpHandler, + configuration: proxyConfig + ); + } + + /// + /// Register HTTP Handler for XProxy Server ... + /// + /// + /// + /// + public static void AddXProxyHttpHandler ( + this IServiceCollection services, + XProxyConfiguration configuration, + HttpClientHandler httpHandler = null + ) { + // + if (configuration.IsNull ()) { + throw XException.InvalidConfiguration.ToException (); + } + + // + if (httpHandler.IsNull ()) { + httpHandler = new HttpClientHandler (); + } + + // + httpHandler.AllowAutoRedirect = configuration.AllowAutoRedirect; + + // + if (configuration.DisableSSLCheck) { + httpHandler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; + } + + // + services + .AddHttpClient (XProxyConstants.XPROXY_HTTP_CLIENT) + .ConfigurePrimaryHttpMessageHandler (() => { + return httpHandler; + }); + } + + /// + /// Register XProxy On Server ... + /// + /// + /// + /// + public static void AddXProxyServer ( + this IServiceCollection services, + IConfiguration configuration, + HttpClientHandler httpHandler = null + ) { + // + var proxyConfig = configuration.GetXProxyConfiguration (); + + // + services.AddXProxyServer ( + httpHandler: httpHandler, + configuration: proxyConfig + ); + } + + /// + /// Register XProxy On Server ... + /// + /// + /// + /// + public static void AddXProxyServer ( + this IServiceCollection services, + XProxyConfiguration configuration, + HttpClientHandler httpHandler = null + ) { + // + // Validate Args ... + if (configuration.IsNull ()) { + throw XException.InvalidConfiguration.ToException (); + } + + // + // Register Proxy Configuration ... + services.AddSingleton (configuration); + + // + // Regiter Proxy Handler ... + services.AddXProxyHttpHandler ( + httpHandler: httpHandler, + configuration: configuration + ); + } + + /// + /// Register XProxy On Server ... + /// + /// + /// + public static void AddXProxyClient ( + this IServiceCollection services, + IConfiguration configuration + ) { + // + var proxyConfig = configuration.GetXProxyConfiguration (); + + // + services.AddXProxyClient ( + configuration: proxyConfig + ); + } + + /// + /// Register XProxy On Client ... + /// + /// + /// + public static void AddXProxyClient ( + this IServiceCollection services, + XProxyConfiguration configuration + ) { + // + // Validate Args ... + if (configuration.IsNull ()) { + throw XException.InvalidConfiguration.ToException (); + } + + // + // Register Proxy Configuration ... + services.AddSingleton (configuration); + + // + // Register IXProxyHelper ... + services.AddSingleton (); + } + + /// + /// Adding XProxy Middleware to Applications ... + /// + /// + public static void UseXProxy (this IApplicationBuilder app) { + app.UseMiddleware (); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Models\XBaseRestResponse.cs + +using RestSharp; + +namespace xIdentityService.Models +{ + public class XBaseRestResponse + { + public T Model { get; set; } + public IRestResponse Response { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Partial\XIdentityProvider+Admin.cs + +using System.Collections.Generic; +using System.Threading.Tasks; +using xCommons.Extensions; +using xHttpService.Constants; +using xIdentityModels.Dtos; +using xIdentityModels.Models; +using xIdentityService.Constants; + +namespace xIdentityService.Providers { + public partial class XIdentityProvider { + // + #region Admin ... + /// + /// Ban Specific Users + /// + /// Authentication Tokens + /// an instance of XUserNameIdRequest which represents user identifier list to Ban + /// a list of banned users identifiers + public async Task> Ban ( + XTokenResponse tokens, + XUserNameIdRequest model + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull ( + tokens, + model + ) + .AddNotEmpty (tokens.AccessToken) + .AddNotZeroChilds (model.Ids) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet (XApiAccountEndpoint.Ban); + request.AddJsonBody (model); + + // + var response = await RunRestRequest> ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// UnBann Specific Users + /// + /// Authentication Tokens + /// an instance of XUserNameIdRequest which represents user identifier list to Ban + /// a list of unbanned users identifiers + public async Task> UnBan ( + XTokenResponse tokens, + XUserNameIdRequest model + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull ( + tokens, + model + ) + .AddNotEmpty (tokens.AccessToken) + .AddNotZeroChilds (model.Ids) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet (XApiAccountEndpoint.UnBan); + request.AddJsonBody (model); + + // + var response = await RunRestRequest> ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Check Specific User is Banned or not + /// + /// Authentication Tokens + /// specified user's identifier + /// a boolean value which represent user banned or not + public async Task IsBanned ( + XTokenResponse tokens, + string userSelectByParam + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens) + .AddNotEmpty ( + userSelectByParam, + tokens.AccessToken + ) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet ( + XApiAccountEndpoint.IsBanned, + @params : new Dictionary { { XParam.XUserSelectByParam.GetStringValue (), userSelectByParam } } + ); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.GET + ); + return response; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Partial\XIdentityProvider+Authentication.cs + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using IdentityModel.Client; +using xCommons.Extensions; +using xExceptions.Constants; +using xHttpService.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIdentityService.Constants; + +namespace xIdentityService.Providers { + public partial class XIdentityProvider { + // + #region Authentication Handlers ... + /// + /// Request for Discovery Document + /// + /// an instance of DiscoveryDocumentResponse + public async Task RequestDiscoveryDocument () { + // + var httpClient = GetHttpClient (); + + // + try { + // + var result = httpClient + .GetDiscoveryDocumentAsync (identityConfiguration.Authority) + .ContinueWith (docTask => { + // + httpClient.Dispose (); + return docTask.Result; + }); + + // + return await result; + } catch { + throw XException.ActionFailed.ToException (); + } + } + + /// + /// Request AccessToken for Specific XApiScope + /// + /// a member of XApiScope + /// an instance of XTokenResponse + public async Task RequestScopeAccessToken (string scope) { + // + // Validate Args ... + var scopeName = string.Empty; + try { + scopeName = scope; // scope.GetStringValue (); + } catch { } + if (scopeName.IsNullOrEmpty ()) { + XException.InvalidArgs.Throw (); + } + + // + var client = GetRestClient (); + var request = GetRestRequet ( + XApiAccountEndpoint.RequestScopeAccessToken, + headers : new Dictionary { { XParam.XScope.GetStringValue (), scope.ToString () } }); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + + // + // Save Token ... + if (!response.IsNull ()) { + await tokenProvider.AddOrUpdateToken (response); + } + + // + return response; + } + + /// + /// Validate a token + /// + /// Authentication Tokens + /// an instance of TokenIntrospectionResponse + public async Task Introspection (XTokenResponse model) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (model) + .AddNotEmpty (model.AccessToken) + .ValidateGroupAsync (); + + // + var httpClient = GetHttpClient (); + + // + try { + // + var discoDoc = await RequestDiscoveryDocument (); + if (discoDoc.IsError) { + XException.ActionFailed.Throw (); + } + + // + var response = await httpClient.IntrospectTokenAsync (new TokenIntrospectionRequest { + // + Address = discoDoc.IntrospectionEndpoint, + ClientId = identityConfiguration.ClientId, + ClientSecret = identityConfiguration.ClientSecret, + + // + Token = model.AccessToken + }); + if (response.IsError) { + throw new Exception (response.Error); + } + + // + return response; + } catch { + throw XException.ActionFailed.ToException (); + } + } + + /// + /// Authenticate a User + /// + /// User Login Required Info, an instance of XLoginRequest + /// an instance of XTokenResponse + public async Task Authenticate (XLoginRequest model) { + // + // Validate Args ... + validationProvider + .GroupValidationBuilder () + .AddNotNull (model) + .AddNotEmpty ( + model.UserSelectBy, + model.Password + ) + .ValidateGroup (); + + // + var client = GetRestClient (); + var request = GetRestRequet (XApiAccountEndpoint.Authenticate); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + + // + // Save Token ... + if (!response.IsNull ()) { + await tokenProvider.AddOrUpdateToken (response); + } + + // + return response; + } + + /// + /// Do Login based on XLoginRequest + /// + /// User Login Required Info, an instance of XLoginRequest + /// an instance of XTokenResponse + public async Task Login (XLoginRequest model) { + // + // Validate Args ... + validationProvider + .GroupValidationBuilder () + .AddNotNull (model, model.Device) + .AddNotEmpty ( + model.UserSelectBy, + model.Password + ) + .ValidateGroup (); + + // + var client = GetRestClient (); + var request = GetRestRequet (XApiAccountEndpoint.Login); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + + // + if (!response.IsNull ()) { + // + var tokens = response.ToXTokenResponse (); + await tokenProvider.AddOrUpdateToken (tokens); + } + + // + return response; + } + + /// + /// Logout from System + /// + /// + /// + public async Task Logout (XTokenResponse model) { + // + await validationProvider + .GroupValidationBuilder () + .AddNotNull (model) + .AddNotEmpty (model.AccessToken) + .ValidateGroupAsync (); + + // + var isExists = await tokenProvider + .IsTokenExists (model.AccessToken); + if (isExists) { + await tokenProvider + .RemoveToken (model.AccessToken); + } + } + + /// + /// Refresh Tokens + /// + /// Authentication Tokens + /// an instance of XTokenResponse + public async Task RefreshTokens (XTokenResponse model) { + // + // Validate Args ... + validationProvider + .GroupValidationBuilder () + .AddNotNull (model) + .AddNotEmpty ( + model.AccessToken, + model.RefreshToken + ) + .ValidateGroup (); + + // + var client = GetRestClient (model); + + // + var request = GetRestRequet ( + XApiAccountEndpoint.RefreshTokens, + headers : model.ToHeaderDictionary ()); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + if (!response.IsNull ()) { + await tokenProvider.UpdateToken (response); + } + + // + return response; + } + #endregion + + // + #region Password Actions Handlers ... + /// + /// Change Password + /// + /// Authentication Tokens + /// an instance of XActionRequest class which provider requirement for action + /// + public async Task ChangePassword ( + XTokenResponse tokens, + XActionRequest model + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens, model) + .AddNotEmpty ( + model.Lang, + model.Password, + model.NewPassword, + model.ReturnUrl) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + // Validate User Select By Param ... + var userSelectByParam = GetUserSelectByParam ( + model, + forceNotNull : true, + excludes : null); + + // + var client = GetRestClient (tokens); + var request = GetRestRequet ( + XApiAccountEndpoint.ChangePassword, + headers : tokens.ToHeaderDictionary ()); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Partial\XIdentityProvider+Friendship.cs + +using System.Collections.Generic; +using System.Threading.Tasks; +using xCommons.Extensions; +using xHttpService.Constants; +using xHttpService.Extensions; +using xIdentityModels.Constants; +using xIdentityModels.Dtos; +using xIdentityModels.Models; +using xIdentityModels.Navigations; +using xIdentityService.Constants; +using xModels.Dtos; + +namespace xIdentityService.Providers +{ + public partial class XIdentityProvider + { + // + #region Friendship ... + /// + /// Follow a User + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + public async Task Follow( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.Follow, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Cancel Following Request + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// a boolean value + public async Task Cancel( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.Cancel, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Unfollow a Follower + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// + public async Task UnFollowFollower( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.UnFollowFollower, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.POST + ); + } + + /// + /// Unfollow Following + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// + public async Task UnFollowFollowing( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.UnFollowFollowing, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.POST + ); + } + + /// + /// Block a Follower + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + public async Task Block( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.Block, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Unblock a Blocked User + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + public async Task UnBlock( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.UnBlock, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Accept a Following Request + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// an instance of XFriendshipFollower + public async Task AcceptRequest( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.AcceptRequest, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Reject a Following Request + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// an instance of XFriendshipFollower + public async Task RejectRequest( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.RejectRequest, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Get Friendship Info Model between Specific User and Current User + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// an instance of XFriendshipInfoDto + public async Task GetFriendshipInfo( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.FriendshipInfo, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Check a User IsFollower of Requested User + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// a boolean value + public async Task IsFollower( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.IsFollower, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Get Specific Follower + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// an instance of XFriendshipFollower + public async Task GetFollower( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.Follower, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Get Follower State of a User + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// an instance of XFriendshipState + public async Task GetFollowerState( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.FollowerState, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Get Followers List Of Current User + /// + /// Authentication Tokens + /// a collection of XFriendshipFollower + public async Task> GetFollowers( + XTokenResponse tokens + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty(tokens.AccessToken) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet(XApiAccountEndpoint.Followers); + + // + var response = await RunRestRequest>( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Get All Followers List Includes Blocked, Requested and etc + /// of Current User + /// + /// Authentication Tokens + /// a collection of XFriendshipFollower + public async Task> GetAllFollowers( + XTokenResponse tokens + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty(tokens.AccessToken) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet(XApiAccountEndpoint.AllFollowers); + + // + var response = await RunRestRequest>( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Return Followers UserName's List of Current User + /// + /// Authentication Tokens + /// a collection of UserNames + public async Task> GetFollowersList( + XTokenResponse tokens + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty(tokens.AccessToken) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet(XApiAccountEndpoint.FollowersList); + + // + var response = await RunRestRequest>( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Retrieve All User's Followers based on Query Model ... + /// + /// Query Model Descriptor + /// Dest User for Reading Followers + /// Requester User's Descriptor + /// + public async Task> QueryFollowers( + XQuery query, + string destUser, + XTokenResponse tokens + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty(destUser) + .AddNotEmpty(tokens.AccessToken) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet(XApiAccountEndpoint.QueryFollowers); + + // + // Attach XQuery to Request ... + request = request.AttachXQuery(query); + request = request.AttachDestUser(destUser); + + // + var response = await RunRestRequest>( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Check a User is in Followings of Current User + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// a boolean value + public async Task IsFollowing( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.IsFollowing, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Get Specific Following Model + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + public async Task GetFollowing( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.Following, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Get Following State Relation between Specific User + /// and Current User + /// + /// Authentication Tokens + /// a user identifier which represent destination user + /// an instance of XFriendshipState + public async Task GetFollowingState( + XTokenResponse tokens, + string destUser + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty( + destUser, + tokens.AccessToken + ) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet( + XApiAccountEndpoint.FollowingState, + @params: new Dictionary { { XParam.XDestUser.GetStringValue (), destUser } + } + ); + + // + var response = await RunRestRequest( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Get Followings of Current User + /// + /// Authentication Tokens + /// a collection of XFriendshipFollowing + public async Task> GetFollowings( + XTokenResponse tokens + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty(tokens.AccessToken) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet(XApiAccountEndpoint.Followings); + + // + var response = await RunRestRequest>( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Get All Following List Includes Blocked, Requested and etc + /// of Current User + /// + /// Authentication Tokens + /// a collection of XFriendshipFollowing + public async Task> GetAllFollowings( + XTokenResponse tokens + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty(tokens.AccessToken) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet(XApiAccountEndpoint.AllFollowings); + + // + var response = await RunRestRequest>( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Return Following UserName's List of Current User + /// + /// Authentication Tokens + /// a collection of UserNames + public async Task> GetFollowingList( + XTokenResponse tokens + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty(tokens.AccessToken) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet(XApiAccountEndpoint.FollowingsList); + + // + var response = await RunRestRequest>( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Retrieve All User's Followings based on Query Model ... + /// + /// Query Model Descriptor + /// Dest User for Reading Followers + /// Requester User's Descriptor + /// + public async Task> QueryFollowings( + XQuery query, + string destUser, + XTokenResponse tokens + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens) + .AddNotEmpty(destUser) + .AddNotEmpty(tokens.AccessToken) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet(XApiAccountEndpoint.QueryFollowings); + + // + // Attach XQuery to Request ... + request = request.AttachXQuery(query); + request = request.AttachDestUser(destUser); + + // + var response = await RunRestRequest>( + client, + request, + XHttpMethod.GET + ); + return response; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Partial\XIdentityProvider+Open.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using MongoDB.Bson; +using xCommons.Constants; +using xCommons.Extensions; +using xExceptions.Constants; +using xHttpService.Constants; +using xIdentityModels.Dtos; +using xIdentityService.Constants; +using xModels.Dtos; + +namespace xIdentityService.Providers +{ + public partial class XIdentityProvider + { + /// + /// Prepare API Based Device to Open Actions ... + /// + /// + public XDeviceDto GetDevice() + { + // + var os = "XSaherElmOS"; + bool isOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + bool isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + if (isOSX) + { + os = "MacOs"; + } + else if (isLinux) + { + os = "Linux"; + } + else if (isWindows) + { + os = "Windows"; + } + var agent = RuntimeInformation.FrameworkDescription; + if (agent.IsNullOrEmpty()) + { + agent = "XSaherElmOSAgent"; + } + var version = RuntimeInformation.OSArchitecture + ", " + RuntimeInformation.OSDescription; + if (version.IsNullOrEmpty()) + { + version = "XSaherElmOSVersion"; + } + var browser = "XSaherElmBrowser_" + RevisionSecretKey.ToMd5String(); + + // + var result = new XDeviceDto + { + Os = os, + Browser = browser, + UserAgent = agent, + OsVersion = version, + DeviceType = XDeviceType.Desktop, + }; + + // + return result; + } + + // + #region OpenGet ... + public async Task OpenGet( + XOpenActionInnerDto model + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(model) + .AddNotEmpty(model.Token) + .AddNotEmpty(model.Payload) + .ValidateGroupAsync(); + + // + var result = await OpenGet( + token: model.Token, + actionRequest: model.Payload + ); + + // + return result; + } + public async Task OpenGet( + string token, + string actionRequest + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotEmpty(token) + .AddNotEmpty(actionRequest) + .ValidateGroupAsync(); + + // + // Retrieve Rest Client ... + var client = GetRestClient(); + + // + // Prepare Rest Request ... + var request = GetRestRequet( + XApiAccountEndpoint.Open, + headers: new Dictionary + { + { XAuthentication.TOKEN, token} + }, + queryStrings: new Dictionary + { + { XParam.XRequest.GetStringValue(), actionRequest } + } + ); + + // + // Run Request and Retrieve Response ... + var response = await RunRestRequestByResponse( + client, + request, + XHttpMethod.GET + ); + + // + // Now we Have to Validate the recieved response ... + + // + // Read token from Headers ... + var responseToken = response.Response.Headers + .First(h => h.Name == XAuthentication.TOKEN).Value.ToString(); + if (responseToken.IsNullOrEmpty()) + { + XException.ActionFailed.Throw(); + } + + // + // Decript Recieve Response ... + var responseJson = response.Model; // SecurityProvider.Decrypt(response.Model); + if (responseJson.IsNullOrEmpty()) + { + XException.ActionFailed.Throw(); + } + + // + var result = new XOpenActionInnerDto + { + Token = responseToken, + Payload = responseJson + }; + + // + // Retrieve Model and Validate it ... + var responseModel = responseJson.FromJSON(); + if (responseModel.IsNull() || + responseModel.Payload.IsNullOrEmpty() || + responseToken != responseModel.Checksum || + responseModel.Timestamp.FromTimestamp().IsExPired()) + { + XException.ActionFailed.Throw(); + } + + // + return result; + } + #endregion + + // + #region GetUserInfo ... + public async Task GetUserInfo( + XDeviceDto device, + string userSelectByParam + ) + { + // + // Validate Args ... + validationProvider.NotNull(device); + validationProvider.NotEmpty(userSelectByParam); + + // + // Prepare Open Action Model ... + var opaModel = new XOpenActionRequestDto + { + Device = device, + Payload = userSelectByParam, + Action = XOpenActions.GetUserInfo, + Timestamp = DateTime.UtcNow.AddMinutes(5).ToTimestamp().ToString() + }; + if (!opaModel.Validate()) + { + XException.InvalidArgs.Throw(); + } + + // + // Fill Checksum Filed ... + var token = opaModel.ToOpenActionToken(); + opaModel.Checksum = token; + + // + // Prepare Request ... + var opaJson = opaModel.ToJson(); + var requestString = SecurityProvider.Encrypt(opaJson); + + // + return await GetUserInfo( + token: token, + request: requestString + ); + } + + public async Task GetUserInfo( + XOpenActionInnerDto dto + ) + { + // + return await GetUserInfo( + token: dto.Token, + request: dto.Payload + ); + } + + public async Task GetUserInfo( + string token, + string request + ) + { + // + // Validate Args ... + validationProvider.NotEmpty(token); + validationProvider.NotEmpty(request); + + // + // Retrieve and Validate Response ... + var response = await OpenGet( + token: token, + actionRequest: request + ); + if (response.IsNull() || + response.Token.IsNullOrEmpty() || + response.Payload.IsNullOrEmpty()) + { + XException.ActionFailed.Throw(); + } + + // + // Now we Have to Converts Repose Payload to Result Model ... + // Note that the respose Model's payload here is Decrypted and + // actually is Json Representation of Result Model ... + var requestModel = response.Payload.FromJSON(); + if (requestModel.IsNull()) + { + XException.ActionFailed.Throw(); + } + + // + var resultJson = SecurityProvider.Decrypt(requestModel.Payload); + var result = resultJson.FromJSON(); + if (result.IsNull()) + { + XException.NotFound.Throw(); + } + + // + return result; + } + #endregion + + // + #region GetUserInfos ... + public async Task> GetUserInfos( + XDeviceDto device, + string ids + ) + { + // + // Validate Args ... + validationProvider.NotNull(device); + validationProvider.NotEmpty(ids); + + // + // Prepare Open Action Model ... + var opaModel = new XOpenActionRequestDto + { + Payload = ids, + Device = device, + Action = XOpenActions.GetUserInfos, + Timestamp = DateTime.UtcNow.AddMinutes(5).ToTimestamp().ToString() + }; + if (!opaModel.Validate()) + { + XException.InvalidArgs.Throw(); + } + + // + // Fill Checksum Filed ... + var token = opaModel.ToOpenActionToken(); + opaModel.Checksum = token; + + // + // Prepare Request ... + var opaJson = opaModel.ToJson(); + var requestString = SecurityProvider.Encrypt(opaJson); + + // + return await GetUserInfos( + token: token, + request: requestString + ); + } + + public async Task> GetUserInfos( + XOpenActionInnerDto dto + ) + { + // + return await GetUserInfos( + token: dto.Token, + request: dto.Payload + ); + } + + public async Task> GetUserInfos( + string token, + string request + ) + { + // + // Validate Args ... + validationProvider.NotEmpty(token); + validationProvider.NotEmpty(request); + + // + // Retrieve and Validate Response ... + var response = await OpenGet( + token: token, + actionRequest: request + ); + if (response.IsNull() || + response.Token.IsNullOrEmpty() || + response.Payload.IsNullOrEmpty()) + { + XException.ActionFailed.Throw(); + } + + // + // Now we Have to Converts Repose Payload to Result Model ... + // Note that the respose Model's payload here is Decrypted and + // actually is Json Representation of Result Model ... + var requestModel = response.Payload.FromJSON(); + if (requestModel.IsNull()) + { + XException.ActionFailed.Throw(); + } + + // + var resultJson = SecurityProvider.Decrypt(requestModel.Payload); + var result = resultJson.FromJSON>(); + if (result.IsNull()) + { + XException.NotFound.Throw(); + } + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Partial\XIdentityProvider+Profile.cs + +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using xCommons.Extensions; +using xHttpService.Constants; +using xHttpService.Extensions; +using xIdentityModels.Dtos; +using xIdentityModels.Models; +using xIdentityModels.Navigations; +using xIdentityService.Constants; +using xModels.Dtos; + +namespace xIdentityService.Providers { + public partial class XIdentityProvider { + // + #region User Actions ... + /// + /// Retrieve User Names based on UserIds + /// + /// Authentication Tokens + /// a comma seperated list of UserIds + /// a collection of UserNames + public async Task> GetUserNames ( + XTokenResponse tokens, + string ids + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens) + .AddNotEmpty (ids, tokens.AccessToken) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + var request = GetRestRequet ( + XApiAccountEndpoint.GetNames, + @params : new Dictionary { { XParam.XIds.GetStringValue (), ids } } + ); + + // + var response = await RunRestRequest> ( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// get user ids and retrieve corresponding user names + /// + /// Authentication Tokens + /// an instance of XUserNameIdRequest which represent required UserIds collection + /// a collection of XUserNameIdResponse instance + public async Task> GetUserNameIds ( + XTokenResponse tokens, + XUserNameIdRequest model + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (model, tokens) + .AddNotZeroChilds (model.Ids) + .AddNotEmpty (tokens.AccessToken) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + var request = GetRestRequet (XApiAccountEndpoint.GetNameIds); + request.AddJsonBody (model); + + // + var response = await RunRestRequest> ( + client, + request, + XHttpMethod.POST + ); + return response; + } + #endregion + + // + #region Profile Actions ... + /// + /// Get Profile Object of Specific User + /// + /// Authentication Tokens + /// determine's which user profile must be retrieved + /// an instance of XUserProfileDto + public async Task GetUserProfile ( + XTokenResponse tokens, + string userSelectByParam + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens) + .AddNotEmpty (userSelectByParam, tokens.AccessToken) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet ( + XApiAccountEndpoint.GetProfile, + @params : new Dictionary { { XParam.XUserSelectByParam.GetStringValue (), userSelectByParam } } + ); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Retrieve Users Based On Query + /// + /// Authentication Tokens + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XUserProfileDto + public async Task> QueryUsers ( + XTokenResponse tokens, + XQuery query + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens, query) + .AddNotEmpty (tokens.AccessToken) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet (XApiAccountEndpoint.QueryProfiles); + + // + // Attach XQuery to Request ... + request = request.AttachXQuery (query); + + // + var response = await RunRestRequest> ( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Retrieve Users Based On Query and Specified role + /// + /// Authentication Tokens + /// an string which represent user role + /// how to filter results based on XQuery structure + /// if it's true the user must has exact role, otherwise top level users also listed + /// an instance of XQueryResult of XUserProfileDto + public async Task> QueryInRoleUsers ( + XTokenResponse tokens, + string role, + XQuery query, + bool forceRole = false + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens, query) + .AddNotEmpty (tokens.AccessToken, role) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet ( + XApiAccountEndpoint.QueryProfiles, + headers : new Dictionary { { XParam.XRole.GetStringValue (), role }, + { XParam.XForceRole.GetStringValue (), forceRole.AsHttpParamString () } + } + ); + + // + // Attach XQuery to Request ... + request = request.AttachXQuery (query); + + // + var response = await RunRestRequest> ( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Query Specified User's Profile Images + /// + /// Authentication Tokens + /// determine's which user profile must be retrieved + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XProfileImage + public async Task> QueryAvatars ( + XTokenResponse tokens, + string userSelectByParam, + XQuery query + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens, query) + .AddNotEmpty (tokens.AccessToken, userSelectByParam) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet ( + XApiAccountEndpoint.QueryAvatars, + @params : new Dictionary { { XParam.XUserSelectByParam.GetStringValue (), userSelectByParam } + } + ); + + // + // Attach XQuery to Request ... + request = request.AttachXQuery (query); + + // + var response = await RunRestRequest> ( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Update User Profile based on XProfileUpdateRequest (FirstName/LastName/DateOfBirth) + /// + /// Authentication Tokens + /// determine's which user profile must be retrieved + /// user update info, an instance of XProfileUpdateRequest + /// an instance of XUserProfileDto + public async Task ProfileUpdateAsync ( + XTokenResponse tokens, + string userSelectByParam, + XProfileUpdateRequest model + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens, model) + .AddNotEmpty (tokens.AccessToken, userSelectByParam) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet ( + XApiAccountEndpoint.UpdateProfile, + @params : new Dictionary { { XParam.XUserSelectByParam.GetStringValue (), userSelectByParam } + } + ); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Update a User Profile based on XProfileUpdateRequest Full + /// + /// Authentication Tokens + /// determine's which user profile must be retrieved + /// user update info, an instance of XProfileUpdateRequest + /// an instance of XUserProfileDto + public async Task FullProfileUpdateAsync ( + XTokenResponse tokens, + string userSelectByParam, + XProfileUpdateRequest model + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens, model) + .AddNotEmpty (tokens.AccessToken, userSelectByParam) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet ( + XApiAccountEndpoint.FullUpdateProfile, + @params : new Dictionary { { XParam.XUserSelectByParam.GetStringValue (), userSelectByParam } + } + ); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Add a New Profile Image + /// + /// Authentication Tokens + /// an specific File to upload, IFormFile + /// an instance of XUserProfileDto + public async Task AddAvatar ( + XTokenResponse tokens, + IFormFile file + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens, file) + .AddNotEmpty (tokens.AccessToken) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet (XApiAccountEndpoint.AddAvatar); + request.AlwaysMultipartFormData = true; + request.AddFileBytes ( + XParam.XFile.GetStringValue (), + file.ToByteArray (), + file.FileName, + file.ContentType + ); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Add a New Collection of Profile Images + /// + /// Authentication Tokens + /// a collection of Files to upload, IFormFileCollection + /// an instance of XUserProfileDto + public async Task AddAvatars ( + XTokenResponse tokens, + IFormFileCollection files + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens) + .AddNotZeroChilds (files) + .AddNotEmpty (tokens.AccessToken) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet (XApiAccountEndpoint.AddAvatars); + request.AlwaysMultipartFormData = true; + + // + // Add Files ... + foreach (var file in files) { + // + // Add Single file ... + request.AddFileBytes ( + XParam.XFiles.GetStringValue (), + file.ToByteArray (), + file.FileName, + file.ContentType + ); + } + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// set Specified Profile Image as Avatar + /// + /// Authentication Tokens + /// an integer which reperesent AvatarId to set as current Avatar + /// an instance of XUserProfileDto + public async Task SetAvatar ( + XTokenResponse tokens, + int id + ) { + // + // Validate Args ... + id.ValidateIntId (); + + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens) + .AddNotEmpty (tokens.AccessToken) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet ( + XApiAccountEndpoint.SetAvatar, + @params : new Dictionary { { XParam.XId.GetStringValue (), id.ToString () } + } + ); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Remove Profile Images + /// + /// Authentication Tokens + /// a comma seperated list of avatarIds to remove + /// an instance of XUserProfileDto + public async Task RemoveAvatars ( + XTokenResponse tokens, + string avatarIds + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens) + .AddNotEmpty (avatarIds, tokens.AccessToken) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet ( + XApiAccountEndpoint.RemoveAvatars, + @params : new Dictionary { { XParam.XIds.GetStringValue (), avatarIds } + } + ); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.DELETE + ); + return response; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Partial\XIdentityProvider+Registration.cs + +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using xCommons.Extensions; +using xExceptions.Constants; +using xHttpService.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIdentityService.Constants; + +namespace xIdentityService.Providers { + public partial class XIdentityProvider { + // + #region Registration ... + /// + /// Check a User Selector is Available for Registration or Not + /// + /// an string value which reperesent a user + /// a boolean value + public async Task CanRegister (string userSelectByParam) { + // + // Validate ARgs ... + validationProvider.NotEmpty (userSelectByParam); + + // + var client = GetRestClient (); + + // + // Check Endpoint ... + var endpoint = XApiAccountEndpoint.CanRegisterUserName; + var @params = new Dictionary { { XParam.XUserName.GetStringValue (), userSelectByParam } }; + + // + // Check UserSelectByType ... + var type = userSelectByParam.GetUserSelectByType (); + switch (type) { + // + case XUserSelectBy.Username: + break; + + // + case XUserSelectBy.MobileNumber: + // + endpoint = XApiAccountEndpoint.CanRegisterMobileNumber; + @params = new Dictionary { { XParam.XMobileNumber.GetStringValue (), userSelectByParam } }; + break; + + // + case XUserSelectBy.Email: + // + endpoint = XApiAccountEndpoint.CanRegisterEmail; + @params = new Dictionary { { XParam.XEmail.GetStringValue (), userSelectByParam } }; + break; + + // + default: + throw XException.InvalidData.ToException (); + } + + // + var request = GetRestRequet ( + endpoint, + @params: @params + ); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Invite a User to Register on Dashboard + /// + /// Authentication Tokens + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + public async Task InviteUser ( + XTokenResponse tokens, + XActionRequest model + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull ( + tokens, + model, + model.Device) + .AddNotEmpty ( + tokens.AccessToken, + model.Lang, + model.Email, + model.ReturnUrl) + .AddEmailAddress (model.Email) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet (XApiAccountEndpoint.InviteUser); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Recieve some Basic Informations and Start Registration Proccess + /// if they Valid + /// + /// Registration Proccess Starts with Invoking this Action + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + public async Task RequestRegistration ( + XActionRequest model + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull ( + model, + model.Device, + model.DateOfBirth) + .AddNotEmpty ( + model.Lang, + model.FirstName, + model.LastName) + .ValidateGroupAsync (); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.RequestRegistration); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Add User Account Informations + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + public async Task AddAccountInfo ( + XActionRequest model + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull ( + model, + model.Device) + .AddNotEmpty ( + model.Lang, + model.UserName, + model.Password, + model.ActionToken) + .ValidateGroupAsync (); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.AddAccountInfo); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Upload and Attach a Profile Image for User. this is an Optional Step + /// + /// user's requested action token + /// an instance of IFormFile for user's Avatar + /// an instance of XActionResponse + public async Task AttachProfileImage ( + string actionToken, + IFormFile file + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (file) + .AddNotEmpty (actionToken) + .ValidateGroupAsync (); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.AttachProfileImage); + + // + // Prepare Request Data ... + request.AddParameter (XParam.XActionToken.GetStringValue (), actionToken); + request.AddFileBytes ( + XParam.XFile.GetStringValue (), + file.ToByteArray (), + file.FileName, + file.ContentType + ); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Finish Registrationn Proccess + /// + /// an instance of XActionRequest class which provider requirement for action + /// + public async Task FinishRegistration (XActionRequest model) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.Password, + model.ReturnUrl) + .AddNotNull (model.Device) + .AddUrl (model.ReturnUrl) + .ValidateGroupAsync (); + + // + // Create Http Client ... + var client = GetRestClient (); + + // + // Create Request ... + var request = GetRestRequet (XApiAccountEndpoint.FinishRegistration); + + // + // Prepare Request Data ... + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + } + + /// + /// Check User is Confirmed Email or not + /// + /// Authentication Tokens + /// an string value which reperesent a user + /// a boolean value + public async Task IsConfirmedEmail ( + XTokenResponse tokens, + string userSelectByParam + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens) + .AddNotEmpty ( + tokens.AccessToken, + userSelectByParam) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet (XApiAccountEndpoint.IsConfirmedEmail); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Check User is Confirmed Mobile or not + /// + /// Authentication Tokens + /// an string value which reperesent a user + /// a boolean value + public async Task IsConfirmedMobile ( + XTokenResponse tokens, + string userSelectByParam + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (tokens) + .AddNotEmpty ( + tokens.AccessToken, + userSelectByParam) + .ValidateGroupAsync (); + + // + var client = GetRestClient (tokens); + + // + var request = GetRestRequet (XApiAccountEndpoint.IsConfirmedMobile); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.GET + ); + return response; + } + + /// + /// Request Registration Confirm + /// + /// an instance of XActionRequest class which provider requirement for action + /// + public async Task RequestConfirmRegistration (XActionRequest model) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.Password, + model.ReturnUrl) + .AddNotNull (model.Device) + .AddUrl (model.ReturnUrl) + .ValidateGroupAsync (); + + // + // Get UserSelect By Param ... + var userSelectByParam = model.GetUserSelectByParam (); + validationProvider.NotEmpty (userSelectByParam); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.RequestConfirmRegistration); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + } + + /// + /// Confirm Registration + /// + /// an instance of XActionRequest class which provider requirement for action + /// + public async Task ConfirmRegistration ( + XActionRequest model + ) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.ActionToken, + model.ReturnUrl + ) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.ConfirmRegistration); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + } + + /// + /// Request For Mobile Number Change/Confirm + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + public async Task RequestConfirmMobile (XActionRequest model) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.Password, + model.MobileNumber + ) + .AddNotNull (model.Device) + .AddMobileNumber (model.MobileNumber) + .ValidateGroupAsync (); + + // + // Get UserSelect By Param ... + var userSelectByParam = GetUserSelectByParam ( + model, + excludes : new List { + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.RequestConfirmMobile); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Confirm Requested Mobile Number + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + public async Task ConfirmMobileNumber (XActionRequest model) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.ActionToken, + model.MobileVerificationCode + ) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.ConfirmMobileNumber); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Request For Email Address Change/Confirm + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + public async Task RequestConfirmEmail (XActionRequest model) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.Email, + model.Password) + .AddNotNull (model.Device) + .AddEmailAddress (model.Email) + .ValidateGroupAsync (); + + // + // Get UserSelect By Param ... + var userSelectByParam = GetUserSelectByParam ( + model, + excludes : new List { + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.RequestConfirmEmail); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Confirm Requested Email Address + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + public async Task ConfirmEmailAddress (XActionRequest model) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.ActionToken, + model.EmailVerificationCode + ) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.ConfirmEmailAddress); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Request Reset Password Action + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + public async Task RequestResetPassword (XActionRequest model) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.ReturnUrl) + .AddNotNull (model.Device) + .AddUrl (model.ReturnUrl) + .ValidateGroupAsync (); + + // + // Get UserSelect By Param ... + var userSelectByParam = GetUserSelectByParam ( + model, + excludes : new List { + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.RequestResetPassword); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + return response; + } + + /// + /// Reset a User's Password + /// + /// an instance of XActionRequest class which provider requirement for action + /// + public async Task ResetPassword (XActionRequest model) { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder () + .AddNotNull (model) + .AddNotEmpty ( + model.Lang, + model.ActionToken, + model.NewPassword, + model.ReturnUrl) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + var client = GetRestClient (); + + // + var request = GetRestRequet (XApiAccountEndpoint.ResetPassword); + request.AddJsonBody (model); + + // + var response = await RunRestRequest ( + client, + request, + XHttpMethod.POST + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Partial\XIdentityProvider+Search.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using xHttpService.Constants; +using xHttpService.Extensions; +using xIdentityModels.Dtos; +using xIdentityModels.Models; +using xIdentityService.Constants; +using xModels.Dtos; + +namespace xIdentityService.Providers +{ + public partial class XIdentityProvider + { + // + #region Actions ... + /// + /// Query Open To Search User Profiles ... + /// + /// requested user's identifiers + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XUserProfileDto + public async Task> QueryOpenToSearchUsers( + XTokenResponse tokens, + XQuery query + ) + { + // + // Validate Args ... + await validationProvider + .GroupValidationBuilder() + .AddNotNull(tokens, query) + .AddNotEmpty(tokens.AccessToken) + .ValidateGroupAsync(); + + // + var client = GetRestClient(tokens); + + // + var request = GetRestRequet(XApiAccountEndpoint.QueryOpenToSearchProfiles); + + // + // Attach XQuery to Request ... + request = request.AttachXQuery(query); + + // + var response = await RunRestRequest>( + client, + request, + XHttpMethod.GET + ); + return response; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Providers\XBaseIdentityHttpProvider.cs + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RestSharp; +using RestSharp.Authenticators; +using xCommons.Constants; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xHttpService.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIdentityService.Constants; +using xIdentityService.Interfaces; +using xIdentityService.Models; +using xIdentityService.Extensions; +using System.Diagnostics.Tracing; +using MySqlX.XDevAPI.Common; + +namespace xIdentityService.Providers +{ + /// + /// an abstraction Implementation for Identity Provided Http Based Services ... + /// + public abstract class XBaseIdentityHttpProvider : IXBaseIdentityHttpProvider + { + /// + /// Client Base Url ... + /// + /// + public string BaseUrl { get; } + + /// + /// Logger ... + /// + /// + public ILogger Logger { get; } + + /// + /// Powered Value ... + /// + /// + public string XPoweredValue { get; } + + /// + /// Default Client Timeout ... + /// + /// + public int DefaultClientTimeout { get; } + + /// + /// Default Buffering Chunk Size ... + /// + /// + public int DefaultBufferingChunckSize { get; } + + /// + /// Validation Provider ... + /// + /// + public XValidationProvider ValidationProvider { get; } + + /// + /// Token Provider ... + /// + private readonly IXTokenProvider tokenProvider; + + /// + /// Identity Provider ... + /// + private readonly IXIdentityProvider identityProvider; + + public XBaseIdentityHttpProvider( + string baseUrl, + ILogger logger, + string poweredValue, + int defaultClientTimeout, + IXTokenProvider tokenProvider, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + int defaultBufferingChunckSize = 8192 + ) + { + // + Logger = logger; + BaseUrl = baseUrl; + XPoweredValue = poweredValue; + this.tokenProvider = tokenProvider; + ValidationProvider = validationProvider; + this.identityProvider = identityProvider; + DefaultClientTimeout = defaultClientTimeout; + DefaultBufferingChunckSize = defaultBufferingChunckSize; + } + + // + #region Tools ... + /// + /// Get an Instance of Http Client + /// + /// + public HttpClient GetHttpClient(XTokenResponse tokens = null) + { + // + HttpClient httpClient = null; + + // + // httpClient = new HttpClient (); + var httpClientHandler = new HttpClientHandler() + { + ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => + { + // + Logger.LogInformation( + $"XSSL Handler: {Environment.NewLine}, sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}" + ); + + // + return true; + }, + ClientCertificateOptions = ClientCertificateOption.Manual, + }; + httpClient = new HttpClient(httpClientHandler); + + // + // Set Authenticator ... + if (!tokens.IsNull() && + !tokens.AccessToken.IsNullOrEmpty()) + { + // + httpClient.DefaultRequestHeaders + .Add( + XAuthorization.Header, + $"{XAuthentication.BEARER} {tokens.AccessToken}"); + } + + // + // Refresh Token ... + if (!tokens.IsNull() && + !tokens.RefreshToken.IsNullOrEmpty()) + { + // + httpClient.DefaultRequestHeaders + .Add( + XAuthorization.RefreshToken, + tokens.RefreshToken); + } + + // + // ExpiresAt ... + if (!tokens.IsNull() && + tokens.ExpiresAt > 0) + { + // + httpClient.DefaultRequestHeaders + .Add( + XAuthorization.ExpiresAt, + tokens.ExpiresAt.AsHttpParamString()); + } + + // + // Set Default Timeout ... + httpClient.Timeout = TimeSpan.FromSeconds(DefaultClientTimeout == -1 ? 10000 : DefaultClientTimeout); + + // + return httpClient; + } + public HttpClient GetHttpClient(XUserClaimsInfoDto userInfo = null) + { + // + XTokenResponse tokens = ToTokenResponse(userInfo); + + // + return GetHttpClient(tokens); + } + + /// + /// Get an Instance of RestClient + /// + /// + public RestClient GetRestClient(XTokenResponse tokens = null) + { + // + var client = new RestClient(BaseUrl); + + // + // Set Authenticator ... + if (!tokens.IsNull() && + !tokens.AccessToken.IsNullOrEmpty()) + { + client.Authenticator = new JwtAuthenticator(tokens.AccessToken); + } + + // + // Refresh Token ... + if (!tokens.IsNull() && + !tokens.RefreshToken.IsNullOrEmpty()) + { + client.AddDefaultHeader(XAuthorization.RefreshToken, tokens.RefreshToken); + } + + // + // ExpiresAt ... + if (!tokens.IsNull() && + tokens.ExpiresAt > 0) + { + client.AddDefaultHeader(XAuthorization.ExpiresAt, tokens.ExpiresAt.AsHttpParamString()); + } + + // + // Set Default Timeout ... + client.Timeout = DefaultClientTimeout; + + // + // SSL Validation Handler ... + client.RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => + { + // + Logger.LogInformation( + $"XSSL Handler: {Environment.NewLine}, sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}" + ); + + // + return true; + }; + + // + return client; + } + public RestClient GetRestClient(XUserClaimsInfoDto userInfo = null) + { + // + XTokenResponse tokens = ToTokenResponse(userInfo); + + // + return GetRestClient(tokens); + } + + /// + /// Get an Instance of RestRequest + /// + /// + /// + /// + /// + /// + /// + public RestRequest GetRestRequet( + Enum endpoint, + bool addXPoweredValue = true, + IDictionary @params = null, + IDictionary headers = null, + IDictionary queryStrings = null + ) + { + // + var url = PrepareUrl( + @params: @params, + endpoint: endpoint, + queryStrings: queryStrings + ); + + // + // Create request ... + var request = new RestRequest(url, DataFormat.Json); + + // + if (addXPoweredValue) + { + request.AddHeader(XAuthorization.XPoweredBy, XPoweredValue); + } + + // + // Add Headers to request ... + if (!headers.IsNull() && headers.Count > 0) + { + // + var enumerator = headers.GetEnumerator(); + while (enumerator.MoveNext()) + { + // + var item = enumerator.Current; + request.AddHeader(item.Key, item.Value); + } + } + + // + return request; + } + + /// + /// Handle Call Specific Request and retrieve Response + /// + /// + /// + /// + /// + /// + /// + /// + public async Task RunRestRequest( + RestClient client, + RestRequest request, + XHttpMethod method, + bool supportRefreshingTokens = true, + CancellationToken cancellationToken = default + ) + { + // + var response = await Task.Run(async () => + { + // + IRestResponse resp = null; + + // + // Call Endpoint Service using Specified Method ... + switch (method) + { + // + // Get ... + case XHttpMethod.GET: + resp = client.Get(request); + break; + + // + // Put ... + case XHttpMethod.PUT: + resp = client.Put(request); + break; + + // + // Post ... + case XHttpMethod.POST: + resp = client.Post(request); + break; + + // + // Delete ... + case XHttpMethod.DELETE: + resp = client.Delete(request); + break; + } + + // + if (!resp.IsSuccessful) + { + // + // Log Exception ... + Logger.LogError($"Error: {resp.ErrorMessage}"); + Logger.LogError($"Exception: {resp.ErrorException.ToJSON()}"); + if (!resp.ErrorMessage.IsNullOrEmpty() && + resp.ErrorMessage.Contains("timed out")) + { + XException.Timeout.Throw(); + } + + // + // TODO: Check UnAuthorized Status here for + // Refresh Tokens or Issue Correct Exception ... + if (supportRefreshingTokens && + resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) + { + // + var isRefreshed = request.Parameters.Any(rp => + rp.Name == XParam.XIsRefreshed.GetStringValue() && + rp.Value.ToString().ToNormalString() == true.ToJSON()); + + // + Logger.LogInformation($"isRefreshed: {isRefreshed}"); + + // + // Get Access Token ... + XTokenResponse tokens = null; + var accessToken = request.Parameters.FirstOrDefault(rp => + rp.Name.ToNormalString() == XAuthorization.Header.ToNormalString()).Value.ToString(); + if (!accessToken.IsNullOrEmpty()) + { + // + accessToken = accessToken.Replace(XAuthorization.TokenIdentifier, ""); + + // + if (!accessToken.IsNullOrEmpty()) + { + tokens = await tokenProvider.RetrieveToken(accessToken); + } + } + + // + // Refresh Token and then re do request ... + if (!isRefreshed && + !accessToken.IsNullOrEmpty() && + !tokens.IsNull() && + tokens.IsRefreshable()) + { + // + request.AddHeader(XParam.XIsRefreshed.GetStringValue(), true.ToJSON()); + + // + // Refreshe Tokens ... + var refreshedTokens = await identityProvider + .RefreshTokens(tokens); + + // + // Renew Rest Client with Refreshed Tokens ... + var timeout = client.Timeout; + client = GetRestClient(refreshedTokens); + client.Timeout = Timeout.Infinite; + + // + // Do Request Again using renew Client ... + return await RunRestRequest( + client: client, + request: request, + method: method); + } + } + + // + var ex = resp.Content.ToXError(); + if (!ex.IsNull()) + { + throw ex.ToException(); + } + + // + // UnAuthorized ... + if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) + { + XException.NotAuthorized.Throw(); + } + + // + // NotFound ... + if (resp.StatusCode == System.Net.HttpStatusCode.NotFound) + { + XException.NotFound.Throw(); + } + + // + XException.ActionFailed.Throw(); + } + + // + var result = resp.Content.FromJSON(); + return result; + }, + cancellationToken: cancellationToken); + + // + return response; + } + + /// + /// Access a Get Request Stream as AsyncEnumerable ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public async Task StreamData( + Enum endpoint, + int bufferLength = 8194, + bool addXPoweredValue = true, + XTokenResponse tokens = null, + IDictionary @params = null, + IDictionary headers = null, + IDictionary queryStrings = null, + [System.Runtime.CompilerServices.EnumeratorCancellation] + CancellationToken cancellationToken = default + ) + { + // + var url = PrepareUrl( + @params: @params, + endpoint: endpoint, + attachBaseUrl: true, + queryStrings: queryStrings + ); + var client = GetHttpClient(tokens); + if (addXPoweredValue) + { + // + client.DefaultRequestHeaders + .Add( + XAuthorization.XPoweredBy, + XPoweredValue + ); + } + + // + // For SSE, you might want to set the Accept header, though not strictly necessary for just streaming + client.DefaultRequestHeaders.Accept.Clear(); + client.DefaultRequestHeaders.Accept + .Add( + new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("text/event-stream")); + + // + var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + response.EnsureSuccessStatusCode(); + + // + var stream = await response.Content.ReadAsStreamAsync(); + + // + return stream; + + // using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken)) + // { + // // + // int readedBuffers = 0; + // response.EnsureSuccessStatusCode(); + // var buffer = new byte[bufferLength]; + // var stream = await response.Content.ReadAsStreamAsync(); + + // // + // while ((readedBuffers = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0) + // { + // // + // var content = buffer.FromBytes(); + // Console.WriteLine(content); + // } + + // // await foreach (var message in stream) { + // // Console.WriteLine(message); + // // } + + // // await foreach (var item in stream.ToAsyncEnumerable(cancellationToken: cancellationToken)) + // // { + // // // + // // Console.WriteLine(item); + + // // // T value = item.IsNull() + // // // ? default(T) + // // // : item.FromBytes().FromJSON(); + + // // // // + // // // yield return value; + // // } + // } + + /// + /// + /// + /// + + // + // var eventSource = new EventSource(url); + // // eventSource. + + // // var client = GetHttpClient(tokens); + // // if (addXPoweredValue) + // // { + // // // + // // client.DefaultRequestHeaders + // // .Add( + // // XAuthorization.XPoweredBy, + // // XPoweredValue); + // // } + + // // // + // // // For SSE, you might want to set the Accept header, though not strictly necessary for just streaming + // // client.DefaultRequestHeaders.Accept.Clear(); + // // client.DefaultRequestHeaders.Accept + // // .Add( + // // new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("text/event-stream")); + + // // // + // // using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken)) + // // { + // // // + // // response.EnsureSuccessStatusCode(); + // // var stream = await response.Content.ReadAsStreamAsync(); + // // await foreach (var item in stream.ToAsyncEnumerable(cancellationToken: cancellationToken)) + // // { + // // // + // // T value = item.IsNull() + // // ? default(T) + // // : item.FromBytes().FromJSON(); + + // // // + // // yield return value; + // // } + // // } + } + + /// + /// Handle Call Specified Request and Retrieve XBaseRestResponse ... + /// + /// + /// + /// + /// + /// + /// + /// an instance of XBaseRestResponse + public async Task> RunRestRequestByResponse( + RestClient client, + RestRequest request, + XHttpMethod method, + bool supportRefreshingTokens = true, + CancellationToken cancellationToken = default + ) + { + // + var response = await Task.Run(async () => + { + // + IRestResponse resp = null; + + // + // Call Endpoint Service using Specified Method ... + switch (method) + { + // + // Get ... + case XHttpMethod.GET: + resp = client.Get(request); + break; + + // + // Put ... + case XHttpMethod.PUT: + resp = client.Put(request); + break; + + // + // Post ... + case XHttpMethod.POST: + resp = client.Post(request); + break; + + // + // Delete ... + case XHttpMethod.DELETE: + resp = client.Delete(request); + break; + } + + // + if (!resp.IsSuccessful) + { + // + // Log Exception ... + Logger.LogError($"Error: {resp.ErrorMessage}"); + Logger.LogError($"Exception: {resp.ErrorException.ToJSON()}"); + if (!resp.ErrorMessage.IsNullOrEmpty() && + resp.ErrorMessage.Contains("timed out")) + { + XException.Timeout.Throw(); + } + + // + // TODO: Check UnAuthorized Status here for + // Refresh Tokens or Issue Correct Exception ... + if (supportRefreshingTokens && + resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) + { + // + var isRefreshed = request.Parameters.Any(rp => + rp.Name == XParam.XIsRefreshed.GetStringValue() && + rp.Value.ToString().ToNormalString() == true.ToJSON()); + + // + Logger.LogInformation($"isRefreshed: {isRefreshed}"); + + // + // Get Access Token ... + XTokenResponse tokens = null; + var accessToken = request.Parameters.FirstOrDefault(rp => + rp.Name.ToNormalString() == XAuthorization.Header.ToNormalString()).Value.ToString(); + if (!accessToken.IsNullOrEmpty()) + { + // + accessToken = accessToken.Replace(XAuthorization.TokenIdentifier, ""); + + // + if (!accessToken.IsNullOrEmpty()) + { + tokens = await tokenProvider.RetrieveToken(accessToken); + } + } + + // + // Refresh Token and then re do request ... + if (!isRefreshed && + !accessToken.IsNullOrEmpty() && + !tokens.IsNull() && + tokens.IsRefreshable()) + { + // + request.AddHeader(XParam.XIsRefreshed.GetStringValue(), true.ToJSON()); + + // + // Refreshe Tokens ... + var refreshedTokens = await identityProvider + .RefreshTokens(tokens); + + // + // Renew Rest Client with Refreshed Tokens ... + var timeout = client.Timeout; + client = GetRestClient(refreshedTokens); + client.Timeout = Timeout.Infinite; + + // + // Do Request Again using renew Client ... + return await RunRestRequestByResponse( + client: client, + request: request, + method: method + ); + } + } + + // + var ex = resp.Content.ToXError(); + if (!ex.IsNull()) + { + throw ex.ToException(); + } + + // + // UnAuthorized ... + if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) + { + XException.NotAuthorized.Throw(); + } + + // + // NotFound ... + if (resp.StatusCode == System.Net.HttpStatusCode.NotFound) + { + XException.NotFound.Throw(); + } + + // + XException.ActionFailed.Throw(); + } + + // + var result = new XBaseRestResponse + { + Response = resp, + Model = resp.Content.FromJSON() + }; + + // + return result; + }, + cancellationToken: cancellationToken); + + // + return response; + } + + /// + /// Get User SelectBy Param + /// + /// + /// + /// + /// + public string GetUserSelectByParam( + XActionRequest model, + bool forceNotNull = true, + ICollection excludes = null + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(model); + + // + var id = model.UserId; + var userName = model.UserName; + var email = model.Email; + var phoneNumber = model.MobileNumber; + + // + if (excludes != null) + { + // + if (excludes.Contains(XUserSelectBy.ID)) + { + id = null; + } + if (excludes.Contains(XUserSelectBy.Email)) + { + email = null; + } + if (excludes.Contains(XUserSelectBy.MobileNumber)) + { + phoneNumber = null; + } + if (excludes.Contains(XUserSelectBy.Username)) + { + userName = null; + } + } + + var selectByParam = + id.IsNullOrEmpty() ? + userName.IsNullOrEmpty() ? + email.IsNullOrEmpty() ? + phoneNumber.IsNullOrEmpty() ? null : phoneNumber : email : userName : id; + + // + // Check result ... + if (forceNotNull && + selectByParam.IsNullOrEmpty()) + { + XException.InvalidData.Throw(); + } + + // + return selectByParam; + } + + /// + /// Prepare Url Address of Specified Enum by Filling Params + /// and Query Strings Attach ... + /// + /// + /// + /// + /// + /// + public string PrepareUrl( + Enum endpoint, + bool attachBaseUrl = false, + IDictionary @params = null, + IDictionary queryStrings = null + ) + { + // + // Retrieving Url Template ... + var url = endpoint.GetStringValue(); + + // + // Filling Parmas and Query Strings of Url ... + url = PrepareUrl( + url: url, + @params: @params, + queryStrings: queryStrings, + attachBaseUrl: attachBaseUrl + ); + + // + return url; + } + + /// + /// Prepare Url Address by Filling Params + /// and Query Strings Attach ... + /// + /// + /// + /// + /// + /// + public string PrepareUrl( + string url, + bool attachBaseUrl = false, + IDictionary @params = null, + IDictionary queryStrings = null + ) + { + // + if (attachBaseUrl) + { + // + var baseUrl = BaseUrl; + if (!baseUrl.EndsWith("/")) + { + baseUrl = $"{baseUrl}/"; + } + + // + url = $"{baseUrl}{url}"; + } + + // + // Add Route Payloads ... + if (!@params.IsNull() && @params.Count > 0) + { + // + var enumerator = @params.GetEnumerator(); + while (enumerator.MoveNext()) + { + // + var item = enumerator.Current; + + // + url = url.Replace(item.Key, item.Value.ToUrlEncoded()); + } + } + + // + // Add Query Strings ... + if (!queryStrings.IsNull() && queryStrings.Count > 0) + { + // + url = $"{url}?"; + var enumerator = queryStrings.GetEnumerator(); + while (enumerator.MoveNext()) + { + // + var item = enumerator.Current; + + // + url += $"{item.Key}={item.Value.ToUrlEncoded()}&"; + } + + // + // remove lates & ... + if (url.EndsWith("&")) + { + url = url.Substring(0, url.Length - 1); + } + } + + // + return url; + } + + /// + /// Converts UserInfo to TokenResponse model ... + /// + /// + /// + public XTokenResponse ToTokenResponse(XUserClaimsInfoDto userInfo = null) + { + // + XTokenResponse result = null; + + // + if (!userInfo.IsNull()) + { + // + result = new XTokenResponse + { + AccessToken = userInfo.AccessToken.IsNullOrEmpty() + ? "" + : userInfo.AccessToken, + RefreshToken = userInfo.RefreshToken.IsNullOrEmpty() + ? "" + : userInfo.RefreshToken, + ExpiresAt = userInfo.ExpiresAt, + }; + + // + if (userInfo.AccessToken.IsNullOrEmpty()) + { + result = null; + } + } + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Providers\XIdentityProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using RestSharp; +using RestSharp.Authenticators; +using xCommons.Constants; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xHttpService.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIdentityService.Configuration; +using xIdentityService.Constants; +using xIdentityService.Interfaces; +using xIdentityService.Models; + +namespace xIdentityService.Providers { + public partial class XIdentityProvider : IXIdentityProvider { + private readonly IXTokenProvider tokenProvider; + private readonly IHostingEnvironment environment; + private JsonSerializerSettings jsonSerializerSettings; + private readonly XValidationProvider validationProvider; + private readonly XIdentityServiceConfiguration identityConfiguration; + + public ILogger Logger { get; } + public IXSecurityProvider SecurityProvider { get; } + + public string BaseUrl { get; } + public string RevisionSecretKey { get; } + public bool ReadResponseAsString { get; set; } + + public XIdentityProvider ( + IXTokenProvider tokenProvider, + IHostingEnvironment environment, + ILogger logger, + IXSecurityProvider securityProvider, + XIdentityServiceConfiguration identityConfiguration, + XValidationProvider validationProvider + ) { + // + BaseUrl = identityConfiguration.Authority; + RevisionSecretKey = identityConfiguration.XRevisionSecretKey; + + // + this.environment = environment; + Logger = logger; + SecurityProvider = securityProvider; + + // + this.identityConfiguration = identityConfiguration; + this.validationProvider = validationProvider; + this.jsonSerializerSettings = new JsonSerializerSettings { + ContractResolver = new CamelCasePropertyNamesContractResolver () + }; + + // + this.tokenProvider = tokenProvider; + } + + // + #region Tools ... + /// + /// Get an Instance of Http Client + /// + /// + public HttpClient GetHttpClient () { + // + HttpClient httpClient = null; + + // + // httpClient = new HttpClient (); + var httpClientHandler = new HttpClientHandler () { + ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { + // + Logger.LogInformation ( + $"XSSL Handler: {Environment.NewLine}, sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}" + ); + + // + return true; + }, + ClientCertificateOptions = ClientCertificateOption.Manual, + }; + httpClient = new HttpClient (httpClientHandler); + + // + // Set Timeout ... + // TODO: Fix this ... + // httpClient.Timeout = TimeSpan + // .FromSeconds (identityConfiguration.DefaultClientTimeout); + + // + return httpClient; + } + + /// + /// Get an Instance of RestClient + /// + /// + public RestClient GetRestClient (XTokenResponse tokens = null) { + // + var client = new RestClient (BaseUrl); + + // + // Set Authenticator ... + if (!tokens.IsNull () && + !tokens.AccessToken.IsNullOrEmpty ()) { + client.Authenticator = new JwtAuthenticator (tokens.AccessToken); + } + + // + // Refresh Token ... + if (!tokens.IsNull () && + !tokens.RefreshToken.IsNullOrEmpty ()) { + client.AddDefaultHeader (XAuthorization.RefreshToken, tokens.RefreshToken); + } + + // + // ExpiresAt ... + if (!tokens.IsNull () && + tokens.ExpiresAt > 0) { + client.AddDefaultHeader (XAuthorization.ExpiresAt, tokens.ExpiresAt.AsHttpParamString ()); + } + + // + // Set Default Timeout ... + client.Timeout = identityConfiguration.DefaultClientTimeout; + + // + // SSL Validation Handler ... + client.RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => { + // + Logger.LogInformation ( + $"XSSL Handler: {Environment.NewLine}, sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}" + ); + + // + return true; + }; + + // + return client; + } + + /// + /// Get an Instance of RestRequest + /// + public RestRequest GetRestRequet ( + XApiAccountEndpoint endpoint, + bool addXPoweredValue = true, + IDictionary @params = null, + IDictionary headers = null, + IDictionary queryStrings = null + ) { + // + var url = endpoint.GetStringValue (); + + // + // Add Route Payloads ... + if (!@params.IsNull () && @params.Count > 0) { + // + var enumerator = @params.GetEnumerator (); + while (enumerator.MoveNext ()) { + // + var item = enumerator.Current; + + // + url = url.Replace (item.Key, item.Value.ToUrlEncoded ()); + } + } + + // + // Add Query Strings ... + if (!queryStrings.IsNull () && queryStrings.Count > 0) { + // + url = $"{url}?"; + var enumerator = queryStrings.GetEnumerator (); + while (enumerator.MoveNext ()) { + // + var item = enumerator.Current; + + // + url += $"{item.Key}={item.Value.ToUrlEncoded ()}&"; + } + + // + // remove lates & ... + if (url.EndsWith ("&")) { + url = url.Substring (0, url.Length - 1); + } + } + + // + // Create request ... + var request = new RestRequest (url, DataFormat.Json); + + // + if (addXPoweredValue) { + request.AddHeader (XAuthorization.XPoweredBy, identityConfiguration.XPoweredValue); + } + + // + // Add Headers to request ... + if (!headers.IsNull () && headers.Count > 0) { + // + var enumerator = headers.GetEnumerator (); + while (enumerator.MoveNext ()) { + // + var item = enumerator.Current; + request.AddHeader (item.Key, item.Value); + } + } + + // + return request; + } + + /// + /// Handle Call Specific Request and retrieve Response + /// + /// + /// + /// + /// + /// + public async Task RunRestRequest ( + RestClient client, + RestRequest request, + XHttpMethod method, + bool supportRefreshingTokens = true + ) { + // + var response = await Task.Run (async () => { + // + IRestResponse resp = null; + + // + // Call Endpoint Service using Specified Method ... + switch (method) { + // + // Get ... + case XHttpMethod.GET: + resp = client.Get (request); + break; + + // + // Put ... + case XHttpMethod.PUT: + resp = client.Put (request); + break; + + // + // Post ... + case XHttpMethod.POST: + resp = client.Post (request); + break; + + // + // Delete ... + case XHttpMethod.DELETE: + resp = client.Delete (request); + break; + } + + // + if (!resp.IsSuccessful) { + // + // Log Exception ... + Logger.LogError ($"Error: {resp.ErrorMessage}"); + Logger.LogError ($"Exception: {resp.ErrorException.ToJSON()}"); + if (!resp.ErrorMessage.IsNullOrEmpty () && + resp.ErrorMessage.Contains ("timed out")) { + XException.Timeout.Throw (); + } + + // + // TODO: Check UnAuthorized Status here for + // Refresh Tokens or Issue Correct Exception ... + if (supportRefreshingTokens && + resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) { + // + var isRefreshed = request.Parameters.Any (rp => + rp.Name == XParam.XIsRefreshed.GetStringValue () && + rp.Value.ToString ().ToNormalString () == true.ToJSON ()); + + // + Logger.LogInformation ($"isRefreshed: {isRefreshed}"); + + // + // Get Access Token ... + XTokenResponse tokens = null; + var accessToken = request.Parameters.FirstOrDefault (rp => + rp.Name.ToNormalString () == XAuthorization.Header.ToNormalString ()).Value.ToString (); + if (!accessToken.IsNullOrEmpty ()) { + // + accessToken = accessToken.Replace (XAuthorization.TokenIdentifier, ""); + + // + if (!accessToken.IsNullOrEmpty ()) { + tokens = await tokenProvider.RetrieveToken (accessToken); + } + } + + // + // Refresh Token and then re do request ... + if (!isRefreshed && + !accessToken.IsNullOrEmpty () && + !tokens.IsNull () && + tokens.IsRefreshable ()) { + // + request.AddHeader (XParam.XIsRefreshed.GetStringValue (), true.ToJSON ()); + + // + // Refreshe Tokens ... + var refreshedTokens = await RefreshTokens (tokens); + + // + // Renew Rest Client with Refreshed Tokens ... + var timeout = client.Timeout; + client = GetRestClient (refreshedTokens); + client.Timeout = Timeout.Infinite; + + // + // Do Request Again using renew Client ... + return await RunRestRequest ( + client: client, + request: request, + method: method); + } + } + + // + var ex = resp.Content.ToXError (); + if (!ex.IsNull ()) { + throw ex.ToException (); + } + + // + // UnAuthorized ... + if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) { + XException.NotAuthorized.Throw (); + } + + // + // NotFound ... + if (resp.StatusCode == System.Net.HttpStatusCode.NotFound) { + XException.NotFound.Throw (); + } + + // + XException.ActionFailed.Throw (); + } + + // + var result = resp.Content.FromJSON (); + return result; + }); + + // + return response; + } + public async Task> RunRestRequestByResponse ( + RestClient client, + RestRequest request, + XHttpMethod method, + bool supportRefreshingTokens = true + ) { + // + var response = await Task.Run (async () => { + // + IRestResponse resp = null; + + // + // Call Endpoint Service using Specified Method ... + switch (method) { + // + // Get ... + case XHttpMethod.GET: + resp = client.Get (request); + break; + + // + // Put ... + case XHttpMethod.PUT: + resp = client.Put (request); + break; + + // + // Post ... + case XHttpMethod.POST: + resp = client.Post (request); + break; + + // + // Delete ... + case XHttpMethod.DELETE: + resp = client.Delete (request); + break; + } + + // + if (!resp.IsSuccessful) { + // + // Log Exception ... + Logger.LogError ($"Error: {resp.ErrorMessage}"); + Logger.LogError ($"Exception: {resp.ErrorException.ToJSON()}"); + if (!resp.ErrorMessage.IsNullOrEmpty () && + resp.ErrorMessage.Contains ("timed out")) { + XException.Timeout.Throw (); + } + + // + // TODO: Check UnAuthorized Status here for + // Refresh Tokens or Issue Correct Exception ... + if (supportRefreshingTokens && + resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) { + // + var isRefreshed = request.Parameters.Any (rp => + rp.Name == XParam.XIsRefreshed.GetStringValue () && + rp.Value.ToString ().ToNormalString () == true.ToJSON ()); + + // + Logger.LogInformation ($"isRefreshed: {isRefreshed}"); + + // + // Get Access Token ... + XTokenResponse tokens = null; + var accessToken = request.Parameters.FirstOrDefault (rp => + rp.Name.ToNormalString () == XAuthorization.Header.ToNormalString ()).Value.ToString (); + if (!accessToken.IsNullOrEmpty ()) { + // + accessToken = accessToken.Replace (XAuthorization.TokenIdentifier, ""); + + // + if (!accessToken.IsNullOrEmpty ()) { + tokens = await tokenProvider.RetrieveToken (accessToken); + } + } + + // + // Refresh Token and then re do request ... + if (!isRefreshed && + !accessToken.IsNullOrEmpty () && + !tokens.IsNull () && + tokens.IsRefreshable ()) { + // + request.AddHeader (XParam.XIsRefreshed.GetStringValue (), true.ToJSON ()); + + // + // Refreshe Tokens ... + var refreshedTokens = await RefreshTokens (tokens); + + // + // Renew Rest Client with Refreshed Tokens ... + var timeout = client.Timeout; + client = GetRestClient (refreshedTokens); + client.Timeout = Timeout.Infinite; + + // + // Do Request Again using renew Client ... + return await RunRestRequestByResponse ( + client: client, + request: request, + method: method + ); + } + } + + // + var ex = resp.Content.ToXError (); + if (!ex.IsNull ()) { + throw ex.ToException (); + } + + // + // UnAuthorized ... + if (resp.StatusCode == System.Net.HttpStatusCode.Unauthorized) { + XException.NotAuthorized.Throw (); + } + + // + // NotFound ... + if (resp.StatusCode == System.Net.HttpStatusCode.NotFound) { + XException.NotFound.Throw (); + } + + // + XException.ActionFailed.Throw (); + } + + // + var result = new XBaseRestResponse + { + Response = resp, + Model = resp.Content.FromJSON () + }; + + // + return result; + }); + + // + return response; + } + + /// + /// Get User SelectBy Param + /// + /// + /// + /// + /// + public string GetUserSelectByParam ( + XActionRequest model, + bool forceNotNull = true, + ICollection excludes = null + ) { + // + // Validate Args ... + validationProvider.NotNull (model); + + // + var id = model.UserId; + var userName = model.UserName; + var email = model.Email; + var phoneNumber = model.MobileNumber; + + // + if (excludes != null) { + // + if (excludes.Contains (XUserSelectBy.ID)) { + id = null; + } + if (excludes.Contains (XUserSelectBy.Email)) { + email = null; + } + if (excludes.Contains (XUserSelectBy.MobileNumber)) { + phoneNumber = null; + } + if (excludes.Contains (XUserSelectBy.Username)) { + userName = null; + } + } + + var selectByParam = + id.IsNullOrEmpty () ? + userName.IsNullOrEmpty () ? + email.IsNullOrEmpty () ? + phoneNumber.IsNullOrEmpty () ? null : phoneNumber : email : userName : id; + + // + // Check result ... + if (forceNotNull && + selectByParam.IsNullOrEmpty ()) { + XException.InvalidData.Throw (); + } + + // + return selectByParam; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Providers\XInMemoryTokenProvider.cs + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using xCommons.Extensions; +using xIdentityModels.Models; +using xIdentityService.Interfaces; + +namespace xIdentityService.Providers { + public partial class XInMemoryTokenProvider : IXTokenProvider { + private IDictionary DbContext; + + public XInMemoryTokenProvider () { + // + this.DbContext = new Dictionary (); + } + + public async Task IsTokenExists (string accessToken) { + // + // Validate Args ... + if (accessToken.IsNullOrEmpty ()) { + return false; + } + + // + // Process Result ... + var result = DbContext.Keys.Any (k => k == accessToken); + return await Task.FromResult (result); + } + + public async Task IsTokenExists (XTokenResponse tokens) { + // + // Validate Args ... + if (tokens.IsNull () || + tokens.AccessToken.IsNullOrEmpty () || + !await IsTokenExists (tokens.AccessToken)) { + return false; + }; + + // + // Process Result ... + var result = await IsTokenExists (tokens.AccessToken); + return result; + } + + public async Task RemoveToken (string accessToken) { + // + // Validate Args ... + if (accessToken.IsNullOrEmpty ()) { + return false; + } + + // + // Process Result ... + var result = DbContext.Remove (accessToken); + return await Task.FromResult (result); + } + + public async Task RemoveToken (XTokenResponse tokens) { + // + // Validate Args ... + if (tokens.IsNull () || + tokens.AccessToken.IsNullOrEmpty () || + !await IsTokenExists (tokens.AccessToken)) { + return false; + }; + + // + // Process Result ... + var result = await RemoveToken (tokens.AccessToken); + return result; + } + + public async Task RetrieveToken (string accessToken) { + // + // Validate Args ... + if (accessToken.IsNullOrEmpty () || + !await IsTokenExists (accessToken)) { + return null; + } + + // + // Process Result ... + var result = DbContext[accessToken]; + return result; + } + + public async Task AddToken (XTokenResponse tokens) { + // + // Validate Args ... + if (tokens.IsNull () || + tokens.AccessToken.IsNullOrEmpty () || + await IsTokenExists (tokens.AccessToken)) { + return false; + }; + + // + // Process Result ... + try { + // + DbContext.Add (tokens.AccessToken, tokens); + return true; + } catch { + return false; + } + } + + public async Task UpdateToken (XTokenResponse tokens) { + // + // Validate Args ... + if (tokens.IsNull () || + tokens.AccessToken.IsNullOrEmpty () || + !await IsTokenExists (tokens.AccessToken)) { + return false; + }; + + // + // Process Result ... + try { + // + DbContext[tokens.AccessToken] = tokens; + return true; + } catch { + return false; + } + } + + public async Task AddOrUpdateToken (XTokenResponse tokens) { + // + // Validate Args ... + if (tokens.IsNull () || + tokens.AccessToken.IsNullOrEmpty () + ) { + return false; + }; + + // + // Process Result ... + try { + // + var isExists = await IsTokenExists (tokens); + if (isExists) { + return await UpdateToken (tokens); + } else { + return await AddToken (tokens); + } + } catch { + return false; + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Providers\XProxyHelper.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Web; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityService.Configuration; +using xIdentityService.Interfaces; + +namespace xIdentityService.Providers { + public class XProxyHelper : IXProxyHelper { + // + #region Props ... + public XProxyConfiguration Configuration { get; } + #endregion + + // + #region Constructor ... + public XProxyHelper (XProxyConfiguration configuration) { + this.Configuration = configuration; + } + #endregion + + // + #region Actions ... + /// + /// Validate XProxy Configuration for Client ... + /// + /// + public bool ValidateConfiguration () { + // + var result = !Configuration.IsNull () && + !Configuration.Host.IsNullOrEmpty () && + Configuration.Host.IsValidUrl () && + Configuration.Routes.HasChild (); + + // + return result; + } + + /// + /// retrie Normalize Host ... + /// + /// + public string NormalizeHost () { + // + var result = string.Empty; + + // + if (!Configuration.IsNull () && + !Configuration.Host.IsNullOrEmpty () && + Configuration.Host.IsValidUrl () + ) { + // + result = Configuration.Host + .ToNormalString (); + + // + if (result.EndsWith ("/")) { + result = result.Substring (0, result.Length - 1); + } + } + + // + return result; + } + + /// + /// retrieve all registred Routes ... + /// + /// + public IEnumerable GetRoutes () { + // + // Validate Args ... + if (Configuration.IsNull ()) { + return null; + } + + // + return Configuration.Routes; + } + + /// + /// Retrieve all Prepared url of Routes ... + /// + /// + public IEnumerable GetRouteUrls () { + // + var normalHost = NormalizeHost (); + if (normalHost.IsNullOrEmpty ()) { + return null; + } + + // + var result = GetRoutes () + .Select (r => $"{normalHost}{r.Route}"); + + // + return result; + } + + /// + /// Find an Specific Route ... + /// + /// + /// + public XProxyRouteDescriptor FindRoute (Expression> whereClause) { + // + // Validate Arg ... + if (whereClause.IsNull ()) { + return null; + } + + // + var whereFunc = whereClause + .Compile (); + + // + var result = GetRoutes () + .FirstOrDefault (whereFunc); + + // + return result; + } + + /// + /// Prepare a request url for a Proxy Route ... + /// + /// + /// + /// + public string PrepareActionUrlForXProxyRoute ( + string url, + XProxyRouteDescriptor descriptor + ) { + // + if (!url.IsValidUrl () || + url.IsNullOrEmpty () || + !ValidateConfiguration () + ) { + throw XException.InvalidArgs.ToException (); + } + + // + // find Route ... + var routeDescriptor = GetRoutes () + .FirstOrDefault (r => r + .IsSameContent (descriptor) + ); + + // + // Validate Route Descriptor ... + if ( + routeDescriptor.IsNull () || + routeDescriptor.Route.IsNullOrEmpty () + ) { + throw XException.InvalidArgs.ToException (); + } + + // + // Retrieve and Validate Host Address ... + var host = NormalizeHost (); + if (!host.IsValidUrl () || + host.IsNullOrEmpty () + ) { + throw XException.InvalidArgs.ToException (); + } + + // + url = $"{host}{routeDescriptor.Route}/{HttpUtility.UrlEncode(url)}"; + + // + return url; + } + + /// + /// Prepare a request url for a Selected Proxy Route ... + /// + /// + /// + /// + public string PrepareActionUrlForXProxyRoute ( + string url, + Expression> whereClause + ) { + // + if (!url.IsValidUrl () || + url.IsNullOrEmpty () || + whereClause.IsNull () || + !ValidateConfiguration () + ) { + throw XException.InvalidArgs.ToException (); + } + + // + var descriptor = FindRoute (whereClause); + + // + // find Route ... + var result = PrepareActionUrlForXProxyRoute ( + url: url, + descriptor: descriptor + ); + + // + return result; + } + #endregion + + // + #region Private ... + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xIdentityService\Security\XSecurityProvider.cs + +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using xCommons.Extensions; +using xCommons.Helpers; +using xCommons.Providers; +using xIdentityService.Configuration; +using xIdentityService.Interfaces; + +namespace xIdentityService.Security { + public partial class XSecurityProvider : IXSecurityProvider { + private readonly string secretKey; + private readonly byte[] iv; + private readonly byte[] key; + private readonly XValidationProvider validationProvider; + + public XSecurityProvider ( + XIdentityServiceConfiguration identityConfiguration, + XValidationProvider validationProvider + ) { + // + this.secretKey = identityConfiguration.XRevisionSecretKey.ToMd5String (); + this.validationProvider = validationProvider; + + // + var ivBytes = new byte[16]; + var keyBytes = new byte[32]; + var secretBytes = Encoding.UTF8.GetBytes (this.secretKey); + + // + Array.Copy ( + secretBytes, + ivBytes, + secretBytes.Length < ivBytes.Length ? + secretBytes.Length : + ivBytes.Length + ); + this.iv = ivBytes; + + // + Array.Copy ( + secretBytes, + keyBytes, + secretBytes.Length < keyBytes.Length ? + secretBytes.Length : + keyBytes.Length + ); + this.key = keyBytes; + + // Encoding.UTF8.GetBytes (this.secretKey); + } + + public string Encrypt (string plainText) { + // + validationProvider.NotEmpty (plainText); + + // + var textBytes = Encoding.UTF8.GetBytes (plainText); + var result = EncryptFromBytes (textBytes); + + // + return result; + } + + public string Decrypt (string cipherText) { + // + validationProvider.NotEmpty (cipherText); + + // + var cipherBytes = Convert.FromBase64String (cipherText); + var result = DecryptFromBytes (cipherBytes); + + // + return result; + } + + public string EncryptFromBytes (byte[] textBytes) { + // + // Validate Args ... + validationProvider.NotNull (textBytes); + + // Declare the string used to hold + // the decrypted text. + byte[] encryptedBytes = null; + + // Create an RijndaelManaged object + // with the specified key and IV. + using (var rijAlg = new RijndaelManaged ()) { + //Settings + rijAlg.Mode = CipherMode.CBC; + rijAlg.Padding = PaddingMode.PKCS7; + // rijAlg.FeedbackSize = 128; + + rijAlg.Key = key; + rijAlg.IV = iv; + + // + using (var encryptor = rijAlg.CreateEncryptor (rijAlg.Key, rijAlg.IV)) { + encryptedBytes = encryptor.TransformFinalBlock (textBytes, 0, textBytes.Length); + } + } + + // + return Convert.ToBase64String (encryptedBytes); + } + + public string DecryptFromBytes (byte[] cipherBytes) { + // + // Validate Args ... + validationProvider.NotNull (cipherBytes); + + // Declare the string used to hold + // the decrypted text. + string plaintext = null; + + // Create an RijndaelManaged object + // with the specified key and IV. + using (var rijAlg = new RijndaelManaged ()) { + //Settings + rijAlg.Mode = CipherMode.CBC; + rijAlg.Padding = PaddingMode.PKCS7; + // rijAlg.FeedbackSize = 128; + + rijAlg.Key = key; + rijAlg.IV = iv; + + // Create a decrytor to perform the stream transform. + var decryptor = rijAlg.CreateDecryptor (rijAlg.Key, rijAlg.IV); + + // Create the streams used for decryption. + using (var msDecrypt = new MemoryStream (cipherBytes)) { + using (var csDecrypt = new CryptoStream (msDecrypt, decryptor, CryptoStreamMode.Read)) { + using (var srDecrypt = new StreamReader (csDecrypt)) { + // Read the decrypted bytes from the decrypting stream + // and place them in a string. + plaintext = srDecrypt.ReadToEnd (); + } + } + } + } + + // + return plaintext; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Constants\ConfigurationNodeNames.cs + +namespace xMessageService.Constants { + public partial struct ConfigurationNodeNames { + public const string MESSAGE_NODE_NAME = "MessageConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Constants\xMessageServiceConstants.cs + +namespace xMessageService.Constants { + public struct xMessageServiceConstants { + public const string DEFAULT_EMPTY_PROVIDER = "EMPTY"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\DI\XDIHelperExtension.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xMessageService.Constants; +using xMessageService.Interfaces; +using xMessageService.Models; +using xMessageService.Providers; + +namespace xMessageService.DI { + public static partial class XDIHelperExtension { + /// + /// Retrieve XMessage Configuration from App Settings + /// + /// + /// + public static XMessageConfiguration GetXMessageConfiguration (this IConfiguration config) { + // + var xMessageConfigSection = config.GetSection (ConfigurationNodeNames.MESSAGE_NODE_NAME); + var xMessageConfiguration = xMessageConfigSection.Get (); + + // + var defaultMailConfiguration = new Dictionary { + ["EMPTY"] = + new XMailConfiguration { + Title = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER, + Host = "", + Port = 25, + EnableSsl = false, + Username = "", + Password = "" + } + }; + + // + var defaultSmsConfiguration = new Dictionary { + ["EMPTY"] = + new XSmsConfiguration { + ServiceUrl = "", + Username = "", + Password = "", + LineNumber = "", + UserApiKey = "", + SecretKey = "", + } + }; + + // + if (xMessageConfiguration.IsNull ()) { + // + xMessageConfiguration = new XMessageConfiguration { + DefaultSMSProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER, + DefaultMailProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER, + MailConfigurations = defaultMailConfiguration, + SmsConfigurations = defaultSmsConfiguration + }; + + // + // Warn Message is Default Configuration ... + Console.WriteLine ($"IMPORTANT: xMessageService Configuration is Default, so the Service can't works properly ..."); + } + + // + // Handle Default Mail Provider ... + if (xMessageConfiguration.DefaultMailProvider.IsNullOrEmpty ()) { + // + if (xMessageConfiguration.IsNull () || + // + xMessageConfiguration.MailConfigurations.IsNull () || + xMessageConfiguration.MailConfigurations.Count == 0) { + // + Console.WriteLine ("There is no defined Mail Provider Configurations ..."); + xMessageConfiguration.DefaultMailProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER; + } + + // + if (xMessageConfiguration.DefaultMailProvider == xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER) { + xMessageConfiguration.MailConfigurations = defaultMailConfiguration; + } else { + xMessageConfiguration.DefaultMailProvider = xMessageConfiguration.MailConfigurations.Keys.First (); + } + } + + // + // Handle Default SMS Provider ... + if (xMessageConfiguration.DefaultSMSProvider.IsNullOrEmpty ()) { + // + if (xMessageConfiguration.IsNull () || + xMessageConfiguration.SmsConfigurations.IsNull () || + xMessageConfiguration.SmsConfigurations.Count == 0) { + // + Console.WriteLine ("There is no defined SMS Provider Configurations ..."); + xMessageConfiguration.DefaultSMSProvider = xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER; + } + + // + if (xMessageConfiguration.DefaultSMSProvider == xMessageServiceConstants.DEFAULT_EMPTY_PROVIDER) { + xMessageConfiguration.SmsConfigurations = defaultSmsConfiguration; + } else { + xMessageConfiguration.DefaultSMSProvider = xMessageConfiguration.SmsConfigurations.Keys.First (); + } + } + + // + return xMessageConfiguration; + } + + /// + /// Register XMessage Service + /// + /// + /// + public static void AddXMessageService (this IServiceCollection services, IConfiguration config) { + // + var xMessageConfiguration = config.GetXMessageConfiguration (); + services.AddSingleton (xMessageConfiguration); + + // + // Register SMS Service ... + var xSmsService = new XSmsService ( + xMessageConfiguration.DefaultSMSProvider, + xMessageConfiguration.SmsConfigurations + ); + services.AddSingleton (xSmsService); + + // + // Register Mail Service ... + var xMailService = new XMailService ( + xMessageConfiguration.DefaultMailProvider, + xMessageConfiguration.MailConfigurations + ); + services.AddSingleton (xMailService); + + // + // Register MEssage Provider ... + services.AddSingleton (); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Interfaces\IXMailService.cs + +using System.Threading.Tasks; +using xMessageService.Models; + +namespace xMessageService.Interfaces { + public partial interface IXMailService { + Task SendMailAsync ( + XMessage message, + string provider = null + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Interfaces\IXMessageProvider.cs + +using System; +using System.Threading.Tasks; +using xMessageService.Models; + +namespace xMessageService.Interfaces { + public partial interface IXMessageProvider { + bool IsReady ( + bool? checkMailService = false, + bool? checkSmsService = false + ); + + Task SendMailAsync ( + XMessage message, + string provider = null, + bool throwException = true, + Exception exception = null + ); + + Task SendSmsAsync ( + XMessage message, + string provider = null, + bool throwException = true, + Exception exception = null + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Interfaces\IXSmsService.cs + +using System.Threading.Tasks; +using xMessageService.Models; + +namespace xMessageService.Interfaces { + public partial interface IXSmsService { + Task SendSmsAsync ( + XMessage message, + string provider = null + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Models\XMailConfiguration.cs + +namespace xMessageService.Models { + public partial class XMailConfiguration { + public string Title { get; set; } + public string Host { get; set; } + public int Port { get; set; } + public bool EnableSsl { get; set; } + public string Username { get; set; } + public string Password { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Models\XMessage.cs + +using System.Collections.Generic; +using xModels.Base; + +namespace xMessageService.Models { + public partial class XMessage : XBaseDto { + public HashSet Recievers { get; set; } = new HashSet (); + public string Message { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Models\XMessageConfiguration.cs + +using System.Collections.Generic; + +namespace xMessageService.Models { + public partial class XMessageConfiguration { + public string DefaultSMSProvider { get; set; } + public string DefaultMailProvider { get; set; } + public IDictionary MailConfigurations { get; set; } + public IDictionary SmsConfigurations { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Models\XSmsConfiguration.cs + +namespace xMessageService.Models { + public partial class XSmsConfiguration { + public string ServiceUrl { get; set; } + public string Username { get; set; } + public string Password { get; set; } + public string LineNumber { get; set; } + public string UserApiKey { get; set; } + public string SecretKey { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Providers\XMailService.cs + +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Mail; +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Constants; +using xMessageService.Interfaces; +using xMessageService.Models; + +namespace xMessageService.Providers { + public partial class XMailService : IXMailService { + private SmtpClient smtpClient; + private readonly string defaultProvider; + private readonly IDictionary configurations; + + public XMailService ( + string defaultProvider, + IDictionary configurations + ) { + this.defaultProvider = defaultProvider; + this.configurations = configurations; + + // + // Get instance of SMTP Client ... + PrepareSmtpClient (); + } + + public Task SendMailAsync ( + XMessage message, + string provider = null + ) { + // + // Run Send Mail Asynchronous ... + return Task.Run (() => { + SendMail (message, provider); + }); + } + + #region Private ... + /// + /// Validate and Retrieve Provider Configuration for Service + /// + /// + /// + private XMailConfiguration GetConfiguration (string provider = null) { + // + if (provider.IsNullOrEmpty ()) { + provider = defaultProvider; + } + + // + if (!configurations.Keys.Contains (provider)) { + XException.MessageServiceInitialFailed.Throw (); + } + + // + return configurations[provider]; + } + + /// + /// Prepare SMTP Client by Using Specified/Default Provider + /// + /// + private void PrepareSmtpClient (string provider = null) { + // + var config = GetConfiguration (provider); + if (config.IsNull()) { + return; + } + + // + smtpClient = new SmtpClient () { + Host = config.Host, + Port = config.Port, + EnableSsl = config.EnableSsl, + UseDefaultCredentials = false, + Credentials = new NetworkCredential ( + config.Username, + config.Password) + }; + } + + /// + /// Prepare Mail Message With Specified/Default Provider + /// + /// + /// + /// + private MailMessage PrepareMailMessage ( + XMessage message, + string provider = null + ) { + // + if (message == null || + message.Message.IsNullOrEmpty () || + message.Recievers.Count == 0) { + XException.InvalidMessage.Throw (); + } + + // + var config = GetConfiguration (provider); + var mailMessage = new MailMessage () { + From = new MailAddress (config.Username), + Subject = config.Title, + Body = message.Message + }; + + // + foreach (var reciever in message.Recievers) { + mailMessage.To.Add (reciever); + } + + // + return mailMessage; + } + + /// + /// Send Mail Using Specified/Default Provider + /// + /// + /// + private void SendMail ( + XMessage message, + string provider = null + ) { + // + if (smtpClient.IsNull()) { + // + Console.WriteLine ($"SMTP Error: MessageServiceInitialFailed ..."); + XException.MessageServiceInitialFailed.Throw (); + } + + // + var mailMessage = PrepareMailMessage (message, provider); + + // + try { + smtpClient.Send (mailMessage); + } catch (Exception ex) { + // + Console.WriteLine ($"SMTP Error: {ex.Message}"); + XException.MessageServiceInitialFailed.Throw (); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Providers\XMessageProvider.cs + +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xExceptions.Constants; +using xMessageService.Interfaces; +using xMessageService.Models; + +namespace xMessageService.Providers { + public partial class XMessageProvider : IXMessageProvider { + private readonly XMessageConfiguration configuration; + private readonly ILogger logger; + private readonly IXSmsService smsService; + private readonly IXMailService mailService; + + public XMessageProvider ( + XMessageConfiguration configuration, + ILogger logger, + IXSmsService smsService, + IXMailService mailService + ) { + this.configuration = configuration; + this.logger = logger; + this.smsService = smsService; + this.mailService = mailService; + } + + /// + /// Send a Mail Message + /// + /// + /// + /// + /// + /// + public async Task SendMailAsync ( + XMessage message, + string provider = null, + bool throwException = true, + Exception exception = null + ) { + // + if (exception.IsNull ()) { + exception = XException.ActionFailed.ToException (); + } + + // + try { + await mailService.SendMailAsync (message, provider); + } catch (Exception ex) { + // + logger.LogError ($"Exeption: {ex.Message}"); + + // + if (throwException) { + throw exception; + } + } + } + + /// + /// Send SMS Message + /// + /// + /// + public async Task SendSmsAsync ( + XMessage message, + string provider = null, + bool throwException = true, + Exception exception = null + ) { + // + if (exception.IsNull ()) { + exception = XException.ActionFailed.ToException (); + } + + // + try { + await smsService.SendSmsAsync (message, provider); + } catch { + if (throwException) { + throw exception; + } + } + } + + /// + /// Check Message Provider is Ready or not + /// + /// + /// + /// + public bool IsReady ( + bool? checkMailService = false, + bool? checkSmsService = false + ) { + // + var result = true; + + // + // Check Mail Service ... + if (checkMailService.HasValue && + checkMailService.GetValueOrDefault ()) { + result = result && mailService != null; + } + + // + // Check Sms Service ... + if (checkSmsService.HasValue && + checkSmsService.GetValueOrDefault ()) { + result = result && smsService != null; + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xMessageService\Providers\XSmsService.cs + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using PhoneNumbers; +using RestSharp; +using xCommons.Extensions; +using xExceptions.Constants; +using xMessageService.Interfaces; +using xMessageService.Models; + +namespace xMessageService.Providers { + public partial class XSmsService : IXSmsService { + private string serviceUrl; + private readonly string defaultProvider; + private readonly IDictionary configurations; + + public XSmsService ( + string defaultProvider, + IDictionary configurations + ) { + this.defaultProvider = defaultProvider; + this.configurations = configurations; + + // + PrepareServiceUrl (); + } + + public Task SendSmsAsync ( + XMessage message, + string provider = null + ) { + // + // Run Send SMS Asynchronous ... + return Task.Run (() => { + SendSMS (message, provider); + }); + } + + #region Private ... + /// + /// Prepare Service Url for Specified/Default Provider + /// + /// + private void PrepareServiceUrl (string provider = null) { + // + if (provider.IsNullOrEmpty ()) { + provider = defaultProvider; + } + + // + var config = GetConfiguration (provider); + if (config.IsNull ()) { + return; + } + + // + serviceUrl = config.ServiceUrl; + serviceUrl += "&username=" + config.Username + "&password=" + config.Password + "&"; + } + + /// + /// Prepare Service url for Integrating XMessage by + /// using Specified/Default Provider Configurations + /// + /// + /// + private void PrepareSmsMessage ( + XMessage message, + string provider = null + ) { + // + if (message == null || + message.Message.IsNullOrEmpty () || + message.Recievers.Count == 0) { + XException.InvalidMessage.Throw (); + } + + // + // Validating Mobile Numbers ... + var phoneNumberUtil = PhoneNumbers.PhoneNumberUtil.GetInstance (); + var recieversList = ""; + + // + PrepareServiceUrl (provider); + + // + foreach (var reciever in message.Recievers) { + try { + // + var mPhoneNumber = phoneNumberUtil.Parse (reciever, null); + if (phoneNumberUtil.IsValidNumber (mPhoneNumber)) { + // + // Format Phone Numbers to the '+989120000000' pattern ... + var recieverPhone = phoneNumberUtil.Format (mPhoneNumber, PhoneNumberFormat.E164); + if (recieversList.Length > 0) { + recieversList += ","; + } + recieversList += reciever; + } + } catch { } + } + + // + var config = GetConfiguration (); + + // + // Preparing All Content and Configurations for Sending Messages ... + serviceUrl += "to=" + + recieversList + + "&message=" + + message.Message + + "&from=" + + config.LineNumber + + "&flash=" + + false; + } + + /// + /// Send a Message as SMS by Specified/Default Provider + /// + /// + /// + private void SendSMS ( + XMessage message, + string provider = null + ) { + // + if (serviceUrl.IsNullOrEmpty ()) { + // + Console.WriteLine ($"SMS Error: MessageServiceInitialFailed ..."); + XException.MessageServiceInitialFailed.Throw (); + } + + // + try { + // + // preparing Service Url based on message data ... + PrepareSmsMessage (message, provider); + + // + // Implementing Service Calling Using RestSharp Library ... + // We Select RestSharp because it supports Sync/Async Calls ... + var client = new RestClient (serviceUrl); + var request = new RestRequest (Method.POST); + + // + // Send Request ... + var response = client.Execute (request); + } catch (Exception ex) { + Console.WriteLine ($"SMS Error: {ex.Message}"); + XException.MessageServiceInitialFailed.Throw (); + } + } + + /// + /// Validate and Retrieve Provider Configuration for Service + /// + /// + /// + private XSmsConfiguration GetConfiguration (string provider = null) { + // + if ( + provider.IsNullOrEmpty () || + provider.ToNormalString () == "empty" + ) { + // + provider = defaultProvider; + return null; + } + + // + if (!configurations.Keys.Contains (provider)) { + XException.MessageServiceInitialFailed.Throw (); + } + + // + return configurations[provider]; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseClass.cs + +using System; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xExceptions.Constants; + +namespace xModels.Base { + /// + /// a Base class which carry required things as a Father for it's childs ... + /// + public abstract class XBaseClass { + // + #region Properties ... + /// + /// LogTag which Specified for this class ... + /// + private readonly string LOG_TAG; + + /// + /// logger instance ... + /// + private readonly ILogger logger; + #endregion + + // + #region Constructor ... + public XBaseClass (ILoggerFactory loggerFactory) { + // + // Preparing Log Tag ... + LOG_TAG = this.GetType ().Name; + + // + Type classType = this.GetType (); + logger = loggerFactory.CreateLogger (classType); + } + #endregion + + // + #region Protected ... + /// + /// Extract Prepared Log MEssage ... + /// + /// + /// + protected virtual string GetLogMessage (string message) { + // + // Prepare Log Message ... + message = $"{LOG_TAG} => {message}"; + return message; + } + + /// + /// Logging specific Message ... + /// + /// + /// + protected virtual void LogMessage ( + string message, + LogLevel logLevel = LogLevel.Information + ) { + // + // Validate Args ... + if (message.IsNullOrEmpty ()) { + throw XException.InvalidArgs + .ToException (); + } + + // + // Prepare Log Message ... + message = GetLogMessage (message); + + // + // Logging Message ... + logger.Log ( + message: message, + logLevel: logLevel + ); + } + + /// + /// Logging specific Message to Console ... + /// + /// + protected virtual void ConsoleMessage (string message) { + // + // Validate Args ... + if (message.IsNullOrEmpty ()) { + throw XException.InvalidArgs + .ToException (); + } + + // + // Prepare Log Message ... + message = GetLogMessage (message); + + // + // Logging Message ... + Console.WriteLine (message); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseDescriptor.cs + +namespace xModels.Base { + /// + /// Descriptors classes Map required data for creating Entities on Seeding time ... + /// this is a Base Descriptor class ... + /// + public abstract class XBaseDescriptor { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseDto.cs + +namespace xModels.Base { + /// + /// DTO - Data Transfer Objects is a kind of classes which Represent Entities ... + /// this is base Dto class ... + /// + public abstract class XBaseDto { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseEntity.cs + +using System.ComponentModel.DataAnnotations; + +namespace xModels.Base { + /// + /// Base Entity Class + /// + /// + public abstract class XBaseEntity { + /// + /// Entity Id + /// + /// entity unique id + [Key] + [Required] + public T Id { get; set; } + + /// + /// Provided Soft Delete Behaviour ... + /// + /// + public bool Deleted { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseEntityDto.cs + +namespace xModels.Base +{ + /// + /// Base Entity Related Dto Class ... + /// + /// + public abstract class XBaseEntityDto + { + /// + /// Entity Id + /// + /// entity unique id + public TKey Id { get; set; } + + /// + /// Provided Soft Delete Behaviour ... + /// + /// + public bool Deleted { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseGuidIDEntity.cs + +using System; + +namespace xModels.Base { + /// + /// a BaseEntity Class which has Id of Guid type + /// + public class XBaseGuidIDEntity : XBaseEntity { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseGuidIDEntityDto.cs + +using System; + +namespace xModels.Base +{ + public class XBaseGuidIDEntityDto : XBaseEntityDto + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseIntIDEntity.cs + +using System.ComponentModel.DataAnnotations; + +namespace xModels.Base { + /// + /// a BaseEntity Class which has Id of int type + /// + public abstract class XBaseIntIDEntity : XBaseEntity { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseIntIDEntityDto.cs + +namespace xModels.Base +{ + public class XBaseIntIDEntityDto : XBaseEntityDto + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseLongIDEntity.cs + +namespace xModels.Base +{ + public class XBaseLongIDEntity : XBaseEntity + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseLongIDEntityDto.cs + +namespace xModels.Base +{ + public class XBaseLongIDEntityDto : XBaseEntityDto + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseRangeRequest.cs + +using System.Collections.Generic; + +namespace xModels.Base { + /// + /// Range Request Classes Carry a List of Objects + /// + /// + public class XBaseRangeRequest { + public IEnumerable Items { get; set; } = new HashSet (); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseStorableDto.cs + +namespace xModels.Base { + public abstract class XBaseStorableDto : XBaseDto { + public abstract TKey Id { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseStorableDtoEventModel.cs + +namespace xModels.Base +{ + public class XBaseStorableDtoEventModel + { + public T Model { get; set; } + + public XBaseStorableDtoEventModel(T model) + { + Model = model; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseStoreEvent.cs + +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using xModels.Interfaces; + +namespace xModels.Base +{ + public abstract class XBaseStoreEvent : IXBaseStoreEvents + where T : XBaseStorableDto + { + // + private readonly ISubject> onAddSubject; + private readonly ISubject> onUpdateSubject; + private readonly ISubject> onRemoveSubject; + + // + private readonly ISubject>> onAddManySubject; + private readonly ISubject>> onUpdateManySubject; + private readonly ISubject>> onRemoveManySubject; + + // + public IObservable> OnAddObservable => onAddSubject.AsObservable(); + + public IObservable> OnUpdateObservable => onUpdateSubject.AsObservable(); + + public IObservable> OnRemoveObservable => onRemoveSubject.AsObservable(); + + // + + public IObservable>> OnAddManyObservable => onAddManySubject.AsObservable(); + + public IObservable>> OnUpdateManyObservable => onUpdateManySubject.AsObservable(); + + public IObservable>> OnRemoveManyObservable => onRemoveManySubject.AsObservable(); + + // + protected XBaseStoreEvent() + { + // + onAddSubject = new ReplaySubject>(1); + onUpdateSubject = new ReplaySubject>(1); + onRemoveSubject = new ReplaySubject>(1); + + // + onAddManySubject = new ReplaySubject>>(1); + onUpdateManySubject = new ReplaySubject>>(1); + onRemoveManySubject = new ReplaySubject>>(1); + } + + // + #region Base Actions ... + public void AddEvent(XBaseStorableDtoEventModel model) + { + onAddSubject.OnNext(model); + } + + public void AddManyEvent(XBaseStorableDtoEventModel> model) + { + onAddManySubject.OnNext(model); + } + + public void RemoveEvent(XBaseStorableDtoEventModel model) + { + onRemoveSubject.OnNext(model); + } + + public void RemoveManyEvent(XBaseStorableDtoEventModel> model) + { + onRemoveManySubject.OnNext(model); + } + + public void UpdateEvent(XBaseStorableDtoEventModel model) + { + onUpdateSubject.OnNext(model); + } + + public void UpdateManyEvent(XBaseStorableDtoEventModel> model) + { + onUpdateManySubject.OnNext(model); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseStringIDEntity.cs + +using System; + +namespace xModels.Base { + /// + /// a BaseEntity Class which has Id of string type + /// + public class XBaseStringIDEntity : XBaseEntity { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseStringIDEntityDto.cs + +namespace xModels.Base +{ + public class XBaseStringIDEntityDto : XBaseEntityDto + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Base\XBaseValueContainer.cs + +namespace xModels.Base +{ + public class XBaseValueContainer + { + public T Value { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Dtos\XBaseIndexedDto.cs + +using xModels.Base; + +namespace xModels.Dtos +{ + public class XBaseIndexedDto : XBaseDto + { + public T Item { get; set; } + public TKey Key { get; set; } + public TIndex Index { get; set; } + } + + public class XBaseIntIndexedDto : XBaseIndexedDto { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Dtos\XDeviceDto.cs + +using xCommons.Constants; +using xModels.Base; + +namespace xModels.Dtos { + public class XDeviceDto : XBaseDto { + /// + /// Operating System + /// + /// device operating system identifier + public string Os { get; set; } + /// + /// Operating System Version + /// + /// which version of operatin system is in use + public string OsVersion { get; set; } + /// + /// Device Browser + /// + /// the Browser identifier + public string Browser { get; set; } + /// + /// Browser Engine Agent + /// + /// browser engine Agent + public string UserAgent { get; set; } + /// + /// Type of client Device + /// + /// represent device type XDeviceType + public XDeviceType DeviceType { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Dtos\XFileDto.cs + +using System; +using System.ComponentModel.DataAnnotations; +using xCommons.Constants; +using xModels.Base; + +namespace xModels.Dtos { + public partial class XFileDto : XBaseDto { + [Required] + public string AuthorId { get; set; } + + public XFileType Type { get; set; } + + [Required] + [StringLength (255)] + public string Name { get; set; } + + [StringLength (255)] + public string Thumb { get; set; } + + [Required] + [StringLength (255)] + public string Path { get; set; } + + [StringLength (255)] + public string ThumbPath { get; set; } + + [Required] + public DateTime UploadedOn { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Dtos\XObjectResponseResult.cs + +namespace xModels.Dtos { + /// + /// Some Requests Get Object Response Result of Data ... + /// + public struct XObjectResponseResult { + + public XObjectResponseResult (T responseObject, string responseText) { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Dtos\XPersonDto.cs + +using xModels.Base; + +namespace xModels.Dtos +{ + /// + /// Since in Many Cases we have to Retrieve Specified User's Minimal + /// Infos for Handle Data Transfering, this Dto is Used ... + /// + public class XPersonDto : XBaseDto + { + public string Username { get; set; } + public string Firstname { get; set; } + public string Lastname { get; set; } + public string Avatar { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Dtos\XQuery.cs + +using xModels.Base; + +namespace xModels.Dtos { + /// + /// Request a Page of Data Based on Query ... + /// this can used in every Data Providers ... + /// + public class XQuery : XBaseDto { + /// + /// an string for filtering results + /// + /// + public string Filter { get; set; } = null; + + /// + /// determines fully load object with it's relational fields or not + /// + /// + public bool ContainsDetail { get; set; } = false; + + /// + /// specifiy sorting of items + /// + /// + public string SortBy { get; set; } = nameof (XBaseIntIDEntity.Id); + + /// + /// specify sorting direction + /// + /// + public bool IsAscending { get; set; } = true; + + /// + /// specify page number + /// + /// + public int Page { get; set; } = 1; + + /// + /// specify page size + /// + /// + public int PageSize { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Dtos\XQueryResult.cs + +using System.Collections.Generic; +using xModels.Base; + +namespace xModels.Dtos { + /// + /// Result of a Requested Page of Data Based on Query ... + /// this can used in every Data Providers ... + /// + public class XQueryResult : XBaseDto { + /// + /// a collection of items for a page ... + /// + /// + public IEnumerable Items { get; set; } + + /// + /// Current Page Number ... + /// + /// + public long Page { get; set; } + + /// + /// Current Page Size ... + /// + /// + public long PageSize { get; set; } + + /// + /// Total Pages ... + /// + /// + public long TotalPages { get; set; } + + /// + /// Total Filtered Items Pages Count ... + /// + /// + public int TotalFilteredPages { get; set; } + + /// + /// Total Filtered Items ... + /// + /// + public long TotalFilteredItems { get; set; } + + /// + /// Total Exists Items ... + /// + /// + public long TotalItems { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Dtos\XResourceDto.cs + +using System.Collections.Generic; +using xModels.Base; + +namespace xModels.Dtos +{ + public class XLocaleResourceDto : XBaseDto + { + public string Language { get; set; } + public string Value { get; set; } + } + + public class XResourceDto : XBaseDto + { + public string Resource { get; set; } + public IEnumerable Locales { get; set; } = new HashSet(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Dtos\XValidateRevisionRequest.cs + +using System.ComponentModel.DataAnnotations; + +namespace xModels.Dtos { + /// + /// in some cases we have to validate a revision of request, this is a model + /// which used to request a revision validator ... + /// + public class XValidateRevisionRequest { + [Required] + public string Revision { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Extensions\DIExtensions.cs + +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xExceptions.Constants; +using xModels.Base; +using xModels.Interfaces; + +namespace xModels.Extensions { + public static class DIExtensions { + /// + /// Register an Specific Store in DI ... + /// + /// DI service collection ... + /// an implementation of inetrface of store ... + /// service lifetime + /// implementation of TType ... + /// an inetrface which implements IXBaseStore + /// type of an instance of XBaseStorable + /// type of key of StorableItem + /// + public static void AddXStore ( + this IServiceCollection services, + TImplementation store, + ServiceLifetime lifeTime = ServiceLifetime.Singleton + ) + where TImplementation : TType + where TType : IXBaseStore + where TStoreItemDto : XBaseStorableDto { + // + // Validate Args ... + if (store.IsNull ()) { + throw XException.InvalidArgs + .ToException (); + } + + // + // Register Service in DI ... + services.Add (new ServiceDescriptor (typeof (TType), typeof (TImplementation), lifeTime)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Extensions\XModelExtensions.cs + +using System.Collections.Generic; +using System.Linq; +using xCommons.Extensions; +using xModels.Dtos; + +namespace xModels.Extensions +{ + public static class XModelExtensions + { + /// + /// Check to XDevice instance to be same or not + /// + /// + /// + /// + public static bool IsSameAs(this XDeviceDto source, XDeviceDto dest) + { + // + if (source.IsNull() || + dest.IsNull()) + { + return false; + } + + // + var isSame = source.Os.ToNormalString() == dest.Os.ToNormalString() && + source.OsVersion.ToNormalString() == dest.OsVersion.ToNormalString() && + source.Browser.ToNormalString() == dest.Browser.ToNormalString() && + source.UserAgent.ToNormalString() == dest.UserAgent.ToNormalString() && + source.DeviceType == dest.DeviceType; + + // + return isSame; + } + + public static IEnumerable AsEnumerable( + this IEnumerable> source + ) + { + return source.Select(i => i.Item); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Interfaces\IXAuditable.cs + +using System; + +namespace xModels.Interfaces { + /// + /// Some times we need to wrap Entities by some logs or audits ... + /// this class provide Audit fields for an Auditable Entity ... + /// + public interface IXAuditable { + string UserAgent { get; } + string UserIp { get; } + DateTime TransactionTime { get; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Interfaces\IXBaseStore.cs + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; +using xModels.Base; + +namespace xModels.Interfaces { + public interface IXBaseStore where T : XBaseStorableDto { + // + #region Retrieve ... + /// + /// Retrieve all Exists Items ... + /// + /// + Task> GetAll (); + + /// + /// Retrieve specific Item by key ... + /// + /// + /// + Task Get (TKey key); + + /// + /// retrieve items based on specific condition ... + /// + /// + /// + Task> FindMany (Expression> condition); + + /// + /// retrieve item based on specific condition ... + /// + /// + /// + Task FindOne (Expression> condition); + #endregion + + // + #region Add ... + /// + /// add specific item to Store ... + /// + /// + /// + Task Add (T item); + + /// + /// Add a Collection of Items on Store ... + /// + /// + /// + Task AddMany (XBaseRangeRequest items); + #endregion + + // + #region Remove ... + /// + /// Remove an item from store ... + /// + /// + /// + Task Remove (T item); + + /// + /// remove an item by it's Key ... + /// + /// + /// + Task RemoveByKey (TKey key); + + /// + /// remove a collection of items at once ... + /// + /// + /// + Task RemoveMany(XBaseRangeRequest items); + #endregion + + // + #region Update ... + /// + /// Update Exists Item in store ... + /// + /// + /// + /// + /// + /// + /// + Task Update ( + T item, + ICollection propertyWhiteList = null, + ICollection propertyBlackList = null, + ICollection>> propertyValueProviders = null, + bool updateWithNullOrEmptyValues = false + ); + + /// + /// Update a Collection of Exists Items ... + /// + /// + /// + /// + /// + /// + /// + Task UpdateMany ( + XBaseRangeRequest items, + ICollection propertyWhiteList = null, + ICollection propertyBlackList = null, + ICollection>> propertyValueProviders = null, + bool updateWithNullOrEmptyValues = false + ); + #endregion + + // + #region Exists ... + /// + /// retrieve item(s) exists based on specific condition ... + /// + /// + /// + Task IsExists (Expression> condition); + + /// + /// is exists an item by providing it's key ... + /// + /// + /// + Task IsExistsByKey (TKey key); + #endregion + + // + #region Count ... + /// + /// Count Exists items ... + /// + /// + Task Count (); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Interfaces\IXBaseStoreEvents.cs + +using System; +using System.Collections.Generic; +using xModels.Base; + +namespace xModels.Interfaces +{ + public interface IXBaseStoreEvents where T : XBaseStorableDto + { + // + void AddEvent(XBaseStorableDtoEventModel model); + void UpdateEvent(XBaseStorableDtoEventModel model); + void RemoveEvent(XBaseStorableDtoEventModel model); + + // + void AddManyEvent(XBaseStorableDtoEventModel> model); + void UpdateManyEvent(XBaseStorableDtoEventModel> model); + void RemoveManyEvent(XBaseStorableDtoEventModel> model); + + // + IObservable> OnAddObservable { get; } + IObservable> OnUpdateObservable { get; } + IObservable> OnRemoveObservable { get; } + + // + IObservable>> OnAddManyObservable { get; } + IObservable>> OnUpdateManyObservable { get; } + IObservable>> OnRemoveManyObservable { get; } + + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xModels\Providers\XBaseInMemoryStore.cs + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using xCommons.Extensions; +using xModels.Base; +using xModels.Interfaces; + +namespace xModels.Providers +{ + public abstract class XBaseInMemoryStore : IXBaseStore + where T : XBaseStorableDto + { + /// + /// this is main store of items ... + /// + /// + /// + private static ConcurrentBag STORE = new ConcurrentBag(); + + // + private readonly IXBaseStoreEvents events; + + // + #region Constructor ... + protected XBaseInMemoryStore( + IXBaseStoreEvents events = null + ) + { + this.events = events; + } + #endregion + + // + #region Retrieve ... + /// + /// Retrieve all Exists Items ... + /// + /// + public Task> GetAll() + { + // + var result = STORE.AsEnumerable(); + return Task.FromResult(result); + } + + /// + /// Retrieve specific Item by key ... + /// + /// + /// + public Task Get(TKey key) + { + // + return IsExistsByKey(key) + .ContinueWith(isExistsTask => + { + // + var isExists = isExistsTask + .RunTask(); + + // + if (!isExists) + { + return null; + } + + // + try + { + var result = STORE + .FirstOrDefault(i => GetKey(i).Equals(key)); + return result; + } + catch + { + return null; + } + }); + } + + /// + /// retrieve items based on specific condition ... + /// + /// + /// + public Task> FindMany(Expression> condition) + { + // + // Validate Args ... + if (condition.IsNull()) + { + return Task.FromResult(new List().AsEnumerable()); + } + + // + var whereFunc = condition.Compile(); + var result = STORE.Where(whereFunc); + + // + return Task.FromResult(result); + } + + /// + /// retrieve item based on specific condition ... + /// + /// + /// + public Task FindOne(Expression> condition) + { + // + // Validate Args ... + if (condition.IsNull()) + { + return null; + } + + // + var whereFunc = condition.Compile(); + var result = STORE.FirstOrDefault(whereFunc); + + // + return Task.FromResult(result); + } + #endregion + + // + #region Add ... + /// + /// add specific item to Store ... + /// + /// + /// + public Task Add(T item) + { + // + // Validate Args ... + if (item.IsNull() || GetKey(item).IsNull()) + { + return Task.FromResult(false); + } + + // + // Try to add item ... + return IsExistsByKey(GetKey(item)) + .ContinueWith(isExistsTask => + { + // + // Check item exists in Store or not ... + var isExists = isExistsTask.RunTask(); + if (isExists) + { + return false; + } + + // + // Add Item to Store ... + STORE.Add(item); + OnAdd(item); + + // + return true; + }); + } + + /// + /// Add a Collection of Items on Store ... + /// + /// + /// + public Task AddMany(XBaseRangeRequest items) + { + // + // Validate Args ... + if ( + items.IsNull() || + !items.Items.HasChild() + ) + { + return Task.FromResult(false); + } + + // + // Retrieve Must Add Items ... + var mustAddItems = items.Items + .Where(i => !GetKey(i) + .IsNull() && !STORE + .Any(si => GetKey(i).Equals(GetKey(si))) + ); + if (!mustAddItems.HasChild()) + { + return Task.FromResult(false); + } + + // + // Add Items ... + try + { + // + mustAddItems + .ToList() + .ForEach(mi => STORE.Add(mi)); + + // + OnAddMany(mustAddItems); + + // + return Task.FromResult(true); + } + catch + { + return Task.FromResult(false); + } + } + #endregion + + // + #region Remove ... + /// + /// Remove an item from store ... + /// + /// + /// + public Task Remove(T item) + { + // + // Validate Args ... + if ( + item.IsNull() || + !STORE + .Any(i => GetKey(i) + .Equals(GetKey(item))) + ) + { + return Task.FromResult(false); + } + + // + try + { + // + RemoveItemFromStore(item); + return Task.FromResult(true); + } + catch + { + return Task.FromResult(false); + } + } + + /// + /// remove an item by it's Key ... + /// + /// + /// + public Task RemoveByKey(TKey key) + { + return Get(key) + .ContinueWith(getTask => + { + // + // retrieve item by it's Key ... + var item = getTask + .RunTask(); + + // + // Check item exists ... + if (item.IsNull()) + { + return false; + } + + // + // Try to remove item ... + var result = false; + try + { + // + result = Remove(item) + .RunTask(); + } + catch + { + result = false; + } + + // + return result; + }); + } + + /// + /// remove a collection of items at once ... + /// + /// + /// + public Task RemoveMany(XBaseRangeRequest items) + { + // + // Validate Args ... + if ( + items.IsNull() || + !items.Items.HasChild() + ) + { + return Task.FromResult(false); + } + + // + var mustRemovedItems = items.Items + .Where(i => STORE + .Any(si => GetKey(si).Equals(GetKey(i))) + ); + if (!mustRemovedItems.HasChild()) + { + return Task.FromResult(false); + } + + // + try + { + // + RemoveItemsFromStore(mustRemovedItems); + return Task.FromResult(true); + } + catch + { + return Task.FromResult(false); + } + } + #endregion + + // + #region Update ... + /// + /// Update Exists Item in store ... + /// + /// + /// + /// + /// + /// + /// + public Task Update( + T item, + ICollection propertyWhiteList = null, + ICollection propertyBlackList = null, + ICollection>> propertyValueProviders = null, + bool updateWithNullOrEmptyValues = false + ) + { + return Get(GetKey(item)) + .ContinueWith(getTask => + { + // + var existsItem = getTask + .RunTask(); + + // + // Validate item Exists ... + if (existsItem.IsNull()) + { + return null; + } + + // + try + { + // + var updatedItem = existsItem; + updatedItem.UpdateData( + updateWith: item, + propertyWhiteList: propertyWhiteList, + propertyBlackList: propertyBlackList, + propertyValueProviders: propertyValueProviders, + updateWithNullOrEmptyValues: updateWithNullOrEmptyValues, + throwExceptionOnFails: true + ); + + // + RemoveItemFromStore(existsItem); + STORE.Add(updatedItem); + OnUpdate(item); + + // + return updatedItem; + } + catch + { + return null; + } + }); + } + + /// + /// Update a Collection of Exists Items ... + /// + /// + /// + /// + /// + /// + /// + public Task UpdateMany( + XBaseRangeRequest items, + ICollection propertyWhiteList = null, + ICollection propertyBlackList = null, + ICollection>> propertyValueProviders = null, + bool updateWithNullOrEmptyValues = false + ) + { + // + // Validate Args ... + if ( + items.IsNull() || + !items.Items.HasChild() + ) + { + return Task.FromResult(false); + } + + // + // retrieve must updated items ... + var mustUpdateItems = items + .Items + .Where(i => STORE + .Any( + si => GetKey(i).Equals(GetKey(si))) + ); + if (!mustUpdateItems.HasChild()) + { + return Task.FromResult(false); + } + + // + // Do Tasks ... + var mustUpdateTasks = mustUpdateItems.Select(i => Update( + i, + propertyWhiteList: propertyWhiteList, + propertyBlackList: propertyBlackList, + propertyValueProviders: propertyValueProviders, + updateWithNullOrEmptyValues: updateWithNullOrEmptyValues + )); + return Task.WhenAll(mustUpdateTasks) + .ContinueWith(updateTasks => + { + // + var updatedItems = updateTasks + .RunTask(); + + // + if (!updatedItems.HasChild()) + { + return false; + } + else + { + // + OnUpdateMany(mustUpdateItems); + return true; + } + }); + } + #endregion + + // + #region Exists ... + /// + /// retrieve item(s) exists based on specific condition ... + /// + /// + /// + public Task IsExists(Expression> condition) + { + // + // Validate Args ... + if (condition.IsNull()) + { + return Task.FromResult(false); + } + + // + var whereFunc = condition.Compile(); + var result = STORE.Any(whereFunc); + + // + return Task.FromResult(result); + } + + /// + /// is exists an item by providing it's key ... + /// + /// + /// + public Task IsExistsByKey(TKey key) + { + return Task.Run(() => + { + return STORE.Any(i => GetKey(i).Equals(key)); + }); + } + #endregion + + // + #region Count ... + /// + /// Count Exists items ... + /// + /// + public Task Count() + { + // + var result = STORE.Count(); + return Task.FromResult(result); + } + #endregion + + // + #region Keys ... + /// + /// set Object Key ... + /// + /// + /// + public void SetKey( + ref T item, + TKey id + ) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + if (keyProp.IsNull()) + { + return; + } + + // + Type t = Nullable.GetUnderlyingType(keyProp.PropertyType) ?? keyProp.PropertyType; + object safeValue = (id == null) ? null : Convert.ChangeType(id, t); + keyProp.SetValue(item, safeValue, null); + } + + /// + /// Get Object Key ... + /// + /// + /// + public TKey GetKey(T item) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + + // + var keyString = string.Empty; + if (keyProp.IsNull()) + { + keyString = string.Empty; + } + else + { + keyString = keyProp.GetValue(item).ToString(); + } + + // + if (keyString.IsNullOrEmpty()) + { + return default(TKey); + } + + // + // Prevent Deserializing issues throug JsonReader ... + if (keyString.IsGuid() && typeof(TKey) == typeof(Guid)) + { + return item.Id; + } + + // + TKey result; + try + { + result = keyString.FromJSON(); + } + catch + { + result = keyString.ConvertTo(); + } + return result; + } + #endregion + + // + #region Private ... + private void RemoveItemFromStore(T item) + { + // + STORE = new ConcurrentBag( + STORE.Except(new[] { item }) + ); + + // + OnRemove(item); + } + + private void RemoveItemsFromStore(IEnumerable items) + { + // + STORE = new ConcurrentBag( + STORE.Except(items) + ); + + // + OnRemoveMany(items); + } + + // + #region Event Notifiers ... + private void OnAdd(T item) + { + // + if (!events.IsNull()) + { + events.AddEvent(new XBaseStorableDtoEventModel(item)); + } + } + private void OnUpdate(T item) + { + // + if (!events.IsNull()) + { + events.UpdateEvent(new XBaseStorableDtoEventModel(item)); + } + } + + private void OnRemove(T item) + { + // + if (!events.IsNull()) + { + events.RemoveEvent(new XBaseStorableDtoEventModel(item)); + } + } + + private void OnAddMany(IEnumerable items) + { + // + if (!events.IsNull()) + { + events.AddManyEvent(new XBaseStorableDtoEventModel>(items)); + } + } + + private void OnUpdateMany(IEnumerable items) + { + // + if (!events.IsNull()) + { + events.UpdateManyEvent(new XBaseStorableDtoEventModel>(items)); + } + } + + private void OnRemoveMany(IEnumerable items) + { + // + if (!events.IsNull()) + { + events.RemoveManyEvent(new XBaseStorableDtoEventModel>(items)); + } + } + #endregion + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Base\XBaseDtoHub.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xModels.Base; +using xPushService.Constants; +using xPushService.Interfaces; + +namespace xPushService.Base +{ + public abstract class XBaseDtoHub : XBaseHub, IXBaseDtoHub + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + #region Constructor ... + protected XBaseDtoHub(ILogger logger) : base(logger) + { } + #endregion + + // + #region Actions ... + /// + /// Notify a New Item added ... + /// + /// + /// + /// + public async Task Add( + TDto item, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.Add.GetStringValue(), + item.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify a New Item added ... + /// + /// + /// + /// + public async Task AddOrUpdate( + TDto item, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.AddOrUpdate.GetStringValue(), + item.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify Add Many Items ... + /// + /// + /// + /// + public async Task AddMany( + IEnumerable items, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.AddOrUpdate.GetStringValue(), + items.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify an Item Deleted ... + /// + /// + /// + /// + public async Task Delete( + TDto item, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.Delete.GetStringValue(), + item.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify Delete Many Items ... + /// + /// + /// + /// + public async Task DeleteMany( + IEnumerable items, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.DeleteMany.GetStringValue(), + items.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify an Item Updated ... + /// + /// + /// + /// + public async Task Update( + TDto item, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.Update.GetStringValue(), + item.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify Update Many Items ... + /// + /// + /// + /// + public async Task UpdateMany( + IEnumerable items, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.UpdateMany.GetStringValue(), + items.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Base\XBaseDtoProvider.cs + +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 Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore.Query; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Interfaces; +using xIdentityService.Interfaces; +using xModels.Base; +using xModels.Dtos; +using xPushService.Constants; +using xPushService.Interfaces; + +namespace xPushService.Base +{ + public abstract class XBaseDtoProvider : IXBaseDtoProvider + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + // + #region Properties ... + public IHubContext Hub { get; } + public IXIdentityProvider IdentityProvider { get; } + public XDataServiceConfiguration DataConfiguration { get; } + public IXBaseRepositoryService Service { get; } + #endregion + + // + #region Constructor ... + protected XBaseDtoProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXBaseRepositoryService service, + IXIdentityProvider identityProvider = null + ) + { + // + Hub = hub; + Service = service; + IdentityProvider = identityProvider; + DataConfiguration = dataConfiguration; + } + #endregion + + // + #region Actions ... + /// + /// add a new Item ... + /// + /// + /// + /// + /// + /// + public virtual async Task AddAsync( + TDto item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.AddAsync( + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + var isValid = !result.IsNullOrDefault(); + if (isValid) + { + // + await SendPush( + connectionId: connectionId, + cancellationToken: cancellationToken, + payLoad: result.ToJSON(camelCase: true), + action: XBaseEntityHubAction.Add.GetStringValue() + ); + } + + // + return result; + } + + /// + /// Update an Item values ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task UpdateAsync( + TKey id, + TDto item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.UpdateAsync( + id: id, + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + var isValid = !result.IsNullOrDefault(); + if (isValid) + { + // + await SendPush( + connectionId: connectionId, + cancellationToken: cancellationToken, + payLoad: result.ToJSON(camelCase: true), + action: XBaseEntityHubAction.Update.GetStringValue() + ); + } + + // + return result; + } + + /// + /// remove an Item by it's Id ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.RemoveAsync( + id: id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + var isValid = !result.IsNullOrDefault(); + if (isValid) + { + // + await SendPush( + connectionId: connectionId, + cancellationToken: cancellationToken, + payLoad: result.ToJSON(camelCase: true), + action: XBaseEntityHubAction.Delete.GetStringValue() + ); + } + + // + return result; + } + + /// + /// count all exists Items ... + /// + /// + /// + /// + public virtual async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + /// + public virtual async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// retrieve an Item by it's Id ... + /// + /// + /// + /// + /// + /// + public virtual async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.GetAsync( + id: id, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// retrieve all exists Items ... + /// + /// + /// + /// + /// + /// + public virtual async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.GetAllAsync( + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// retrieve all exists Dtos + /// as Async Enumerable ... + /// + /// + /// + /// + /// + /// + public virtual async IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default + ) + { + // + var enumerable = Service.GetAllAsAsyncEnumerable( + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + await foreach (var dto in enumerable) + { + // + if (cancellationToken.IsCancellationRequested) + { + yield break; + } + + // + yield return dto; + } + } + + /// + /// find an Item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public virtual async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.FindOneAsync( + predicate: predicate, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// find a collection of Items by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.FindManyAsync( + predicate: predicate, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public virtual async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.QueryAsync( + query: query, + predicate: predicate, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + #endregion + + // + #region Hub Actions ... + /// + /// Send Custom Push Message ... + /// + /// + /// + /// + /// + /// + public async Task SendPush( + string action, + string payLoad, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var actions = new List + { + XBaseEntityHubAction.Add.GetStringValue(), + XBaseEntityHubAction.Update.GetStringValue(), + XBaseEntityHubAction.Delete.GetStringValue(), + XBaseEntityHubAction.AddMany.GetStringValue(), + XBaseEntityHubAction.DeleteMany.GetStringValue(), + XBaseEntityHubAction.UpdateMany.GetStringValue(), + XBaseEntityHubAction.AddOrUpdate.GetStringValue(), + }; + + // + // Validate ... + var isValid = + !Hub.IsNull() && + !action.IsNullOrEmpty() && + !payLoad.IsNullOrEmpty() && + actions.Contains(action); + if (!isValid) + { + return; + } + + // + // Retrieve Connection Id ... + var clients = Hub.Clients.All; + if (!connectionId.IsNullOrEmpty()) + { + clients = Hub.Clients.AllExcept(connectionId); + } + + // + await clients.SendAsync( + action, + payLoad, + connectionId, + cancellationToken + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Base\XBaseEntityHub.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xModels.Base; +using xPushService.Constants; +using xPushService.Interfaces; + +namespace xPushService.Base +{ + public abstract class XBaseEntityHub : XBaseHub, IXBaseEntityHub + where TEntity : XBaseEntity + { + // + #region Constructor ... + protected XBaseEntityHub(ILogger logger) : base(logger) + { } + #endregion + + // + #region Actions ... + /// + /// Notify a New Entity added ... + /// + /// + /// + /// + public async Task Add( + TEntity entity, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.Add.GetStringValue(), + entity.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify a New Entity added ... + /// + /// + /// + /// + public async Task AddOrUpdate( + TEntity entity, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.AddOrUpdate.GetStringValue(), + entity.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify Add Many Entities ... + /// + /// + /// + /// + public async Task AddMany( + IEnumerable entities, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.AddOrUpdate.GetStringValue(), + entities.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify an Entity Deleted ... + /// + /// + /// + /// + public async Task Delete( + TEntity entity, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.Delete.GetStringValue(), + entity.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify Delete Many Entities ... + /// + /// + /// + /// + public async Task DeleteMany( + IEnumerable entities, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.DeleteMany.GetStringValue(), + entities.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify an Entity Updated ... + /// + /// + /// + /// + public async Task Update( + TEntity entity, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.Update.GetStringValue(), + entity.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify Update Many Entities ... + /// + /// + /// + /// + public async Task UpdateMany( + IEnumerable entities, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.UpdateMany.GetStringValue(), + entities.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Base\XBaseEntityProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore.Query; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Interfaces; +using xIdentityService.Interfaces; +using xModels.Base; +using xModels.Dtos; +using xPushService.Constants; +using xPushService.Interfaces; + +namespace xPushService.Base +{ + public abstract class XBaseEntityProvider : IXBaseEntityProvider + where TEntity : XBaseEntity + where THub : XBaseEntityHub + { + // + #region Properties ... + public IHubContext Hub { get; } + public IXIdentityProvider IdentityProvider { get; } + public IXBaseRepository Repository { get; } + public XDataServiceConfiguration DataConfiguration { get; } + #endregion + + // + #region Constructor ... + protected XBaseEntityProvider( + IHubContext hub, + IXBaseRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) + { + // + Hub = hub; + Repository = repository; + IdentityProvider = identityProvider; + DataConfiguration = dataConfiguration; + } + #endregion + + // + #region Actions ... + /// + /// add a new Item ... + /// + /// + /// + /// + /// + /// + public virtual async Task AddAsync( + TEntity item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.AddAsync( + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + var isValid = !result.IsNullOrDefault(); + if (isValid) + { + // + await SendPush( + connectionId: connectionId, + cancellationToken: cancellationToken, + payLoad: result.ToJSON(camelCase: true), + action: XBaseEntityHubAction.Add.GetStringValue() + ); + } + + // + return result; + } + + /// + /// Update an Item values ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task UpdateAsync( + TKey id, + TEntity item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.UpdateAsync( + id: id, + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + var isValid = !result.IsNullOrDefault(); + if (isValid) + { + // + await SendPush( + connectionId: connectionId, + cancellationToken: cancellationToken, + payLoad: result.ToJSON(camelCase: true), + action: XBaseEntityHubAction.Update.GetStringValue() + ); + } + + // + return result; + } + + /// + /// remove an Item by it's Id ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.RemoveAsync( + id: id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + var isValid = !result.IsNullOrDefault(); + if (isValid) + { + // + await SendPush( + connectionId: connectionId, + cancellationToken: cancellationToken, + payLoad: result.ToJSON(camelCase: true), + action: XBaseEntityHubAction.Delete.GetStringValue() + ); + } + + // + return result; + } + + /// + /// count all exists Items ... + /// + /// + /// + /// + public virtual async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + /// + public virtual async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// retrieve an Item by it's Id ... + /// + /// + /// + /// + /// + /// + public virtual async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.GetAsync( + id: id, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// retrieve all exists Items ... + /// + /// + /// + /// + /// + /// + public virtual async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.GetAllAsync( + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// find an Item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public virtual async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.FindOneAsync( + predicate: predicate, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// find a collection of Items by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.FindManyAsync( + predicate: predicate, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public virtual async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.QueryAsync( + query: query, + predicate: predicate, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + #endregion + + // + #region Hub Actions ... + /// + /// Send Custom Push Message ... + /// + /// + /// + /// + /// + /// + public async Task SendPush( + string action, + string payLoad, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var actions = new List + { + XBaseEntityHubAction.Add.GetStringValue(), + XBaseEntityHubAction.Update.GetStringValue(), + XBaseEntityHubAction.Delete.GetStringValue(), + XBaseEntityHubAction.AddMany.GetStringValue(), + XBaseEntityHubAction.DeleteMany.GetStringValue(), + XBaseEntityHubAction.UpdateMany.GetStringValue(), + XBaseEntityHubAction.AddOrUpdate.GetStringValue(), + }; + + // + // Validate ... + var isValid = + !Hub.IsNull() && + !action.IsNullOrEmpty() && + !payLoad.IsNullOrEmpty() && + actions.Contains(action); + if (!isValid) + { + return; + } + + // + // Retrieve Connection Id ... + var clients = Hub.Clients.All; + if (!connectionId.IsNullOrEmpty()) + { + clients = Hub.Clients.AllExcept(connectionId); + } + + // + await clients.SendAsync( + action, + payLoad, + connectionId, + cancellationToken + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Base\XBaseHub.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xPushService.Constants; +using xPushService.Interfaces; + +namespace xPushService.Base +{ + public abstract class XBaseHub : Hub, IXBaseHub + { + // + #region Props ... + protected readonly ILogger logger; + #endregion + + // + #region Constructor ... + public XBaseHub( + ILogger logger + ) + { + this.logger = logger; + } + #endregion + + // + #region Overrides ... + public override async Task OnConnectedAsync() + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + // Notify Other Clients Which a new Connection Joins ... + await Clients.AllExcept(connectionId).SendAsync(XBaseHubAction.NewConnection.GetStringValue(), connectionId); + } + + // + await base.OnConnectedAsync(); + } + + public override async Task OnDisconnectedAsync(Exception exception) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + // Notify Other Clients Which a Connection Closed ... + await Clients.AllExcept(connectionId).SendAsync(XBaseHubAction.ConnectionClosed.GetStringValue(), connectionId); + } + await base.OnDisconnectedAsync(exception); + } + #endregion + + // + #region Actions ... + public async Task SendCustomAction( + string actionName, + string payLoad, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + await Clients.AllExcept(connectionId).SendAsync( + actionName, + payLoad, + connectionId, + cancellationToken + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Base\XBaseProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore.Query; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Interfaces; +using xIdentityService.Interfaces; +using xModels.Base; +using xModels.Dtos; +using xPushService.Constants; +using xPushService.Interfaces; + +namespace xPushService.Base +{ + public abstract class XBaseProvider : IXBaseProvider + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + #region Props ... + public IXIdentityProvider IdentityProvider { get; } + public XDataServiceConfiguration DataConfiguration { get; } + public IHubContext> Hub { get; } + public IXBaseRepositoryService RepositoryService { get; } + #endregion + + // + #region Actions ... + /// + /// add a new Dto ... + /// + /// + /// + /// + /// + /// + public async Task AddAsync( + TDto item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await RepositoryService.AddAsync( + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + if (!result.IsNullOrDefault()) + { + // + await SendPush( + action: XBaseEntityHubAction.Add.GetStringValue(), + payLoad: result.ToJSON(camelCase: true), + connectionId: connectionId + ); + } + + // + return result; + } + + /// + /// Update an Dto values ... + /// + /// + /// + /// + /// + /// + /// + public async Task UpdateAsync( + TKey id, + TDto item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await RepositoryService.UpdateAsync( + id: id, + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + if (!result.IsNullOrDefault()) + { + // + await SendPush( + action: XBaseEntityHubAction.Update.GetStringValue(), + payLoad: result.ToJSON(camelCase: true), + connectionId: connectionId + ); + } + + // + return result; + } + + /// + /// remove an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await RepositoryService.RemoveAsync( + id: id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + if (!result.IsNullOrDefault()) + { + // + await SendPush( + action: XBaseEntityHubAction.Update.GetStringValue(), + payLoad: result.ToJSON(camelCase: true), + connectionId: connectionId + ); + } + + // + return result; + } + + /// + /// count all exists Dtos ... + /// + /// + /// + /// + public async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await RepositoryService.CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// Check a Dto exists or not ... + /// + /// + /// + /// + /// + public async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await RepositoryService.IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// retrieve an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await RepositoryService.GetAsync( + id: id, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// retrieve all exists Dtos ... + /// + /// + /// + /// + /// + /// + public async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await RepositoryService.GetAllAsync( + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// find an Dto by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await RepositoryService.FindOneAsync( + predicate: predicate, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// find a collection of Dtos by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await RepositoryService.FindManyAsync( + predicate: predicate, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// retrieve Dtos based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await RepositoryService.QueryAsync( + query: query, + predicate: predicate, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + #endregion + + // + #region Hub Actions ... + /// + /// Send Custom Push Message ... + /// + /// + /// + /// + /// + /// + public async Task SendPush( + string action, + string payLoad, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var actions = new List + { + XBaseEntityHubAction.Add.GetStringValue(), + XBaseEntityHubAction.Update.GetStringValue(), + XBaseEntityHubAction.Delete.GetStringValue(), + XBaseEntityHubAction.AddMany.GetStringValue(), + XBaseEntityHubAction.DeleteMany.GetStringValue(), + XBaseEntityHubAction.UpdateMany.GetStringValue(), + XBaseEntityHubAction.AddOrUpdate.GetStringValue(), + }; + + // + // Validate ... + var isValid = + !Hub.IsNull() && + !action.IsNullOrEmpty() && + actions.Contains(action); + if (!isValid) + { + return; + } + + // + var clients = Hub.Clients.All; + if (!connectionId.IsNullOrEmpty()) + { + clients = Hub.Clients.AllExcept(connectionId); + } + + // + try + { + // + await clients.SendAsync( + action, + payLoad, + connectionId, + cancellationToken + ); + } + catch { } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Base\XBaseWebRTCHub.cs + +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using xPushService.Interfaces; +using Microsoft.AspNetCore.SignalR; +using xPushService.Constants; +using xCommons.Extensions; +using System.Threading; + +namespace xPushService.Base +{ + public abstract class XBaseWebRTCHub : XBaseHub, IXBaseWebRTCHub + { + // + #region Props ... + #endregion + + // + #region Constructor ... + protected XBaseWebRTCHub( + ILogger logger + ) : base(logger) + { } + #endregion + + // + #region WebRTCActions ... + /// + /// Sending WebRTC Offer To Specified Connection ... + /// + /// + /// + /// + /// + public async Task Offer( + string offer, + string to, + CancellationToken cancellationToken = default + ) + { + // + await Clients.Client(to).SendAsync( + XWebRTCAction.Offer.GetStringValue(), + offer, + cancellationToken + ); + } + + /// + /// Sending WebRTC Answer to Specified Connection ... + /// + /// + /// + /// + public async Task Answer( + string answer, + CancellationToken cancellationToken = default + ) + { + // + await Clients.All.SendAsync( + XWebRTCAction.Answer.GetStringValue(), + answer, + Context.ConnectionId, + cancellationToken + ); + } + + /// + /// Sending WebRTC Candidate to Specified Connecton ... + /// + /// + /// + /// + /// + public async Task Candidate( + string candidate, + string to, + CancellationToken cancellationToken = default + ) + { + // + string actionName = XWebRTCAction.Candidate.GetStringValue(); + if (!string.IsNullOrEmpty(to)) + { + // + await Clients.Client(to).SendAsync( + actionName, + candidate, + Context.ConnectionId, + cancellationToken + ); + } + else + { + // + await Clients.All.SendAsync( + actionName, + candidate, + Context.ConnectionId, + cancellationToken + ); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Configurations\XPushServiceConfiguration.cs + +namespace xPushService.Configurations { + public partial class XPushServiceConfiguration { + /// + /// Base route of WebSocket Server ... + /// + /// string + public string BaseRoute { get; set; } + + /// + /// determines log level of signalR ... + /// + /// + public object ConnectionLogLevel { get; set; } + + /// + /// when client disconnected it's automatically try to reconnect, this + /// determines max number of try to connect ... + /// + /// + public int PushConnectionMaxRetry { get; set; } + + /// + /// add support for message protocol ... + /// + /// + public bool AddSupportMessageProtocol { get; set; } + + /// + /// the delay between two connection try .. + /// + /// + public int PushConnectionReconnectDelay { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Constants\XPushAction.cs + +using xExceptions.Attributes; + +namespace xPushService.Constants +{ + public enum XBaseHubAction + { + /// + /// new Connection Join ... + /// + [StringValue("NewConnection")] + NewConnection, + /// + /// an Exists Connection Closed ... + /// + [StringValue("ConnectionClosed")] + ConnectionClosed, + } + + /// + /// All Actions in WebRTC Hubs ... + /// + public enum XWebRTCAction + { + // + [StringValue("Offer")] + Offer, + + [StringValue("Answer")] + Answer, + + [StringValue("Candidate")] + Candidate, + } + + /// + /// Provides All Actions Based on Entities ... + /// + public enum XBaseEntityHubAction + { + [StringValue("Add")] + Add, + [StringValue("AddOrUpdate")] + AddOrUpdate, + [StringValue("AddMany")] + AddMany, + [StringValue("Update")] + Update, + [StringValue("UpdateMany")] + UpdateMany, + [StringValue("Delete")] + Delete, + [StringValue("DeleteMany")] + DeleteMany, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Constants\XPushServiceConstants.cs + +namespace xPushService.Constants { + public partial class XPushServiceConstants { + public partial struct XConfigurationNodes { + public const string XPushServiceConfiguration = "PushServiceConfiguration"; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Controllers\XBaseProvidedController.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; +using xPushService.Interfaces; + +namespace xPushService.Controllers +{ + [AllowAnonymous] + public abstract class XBaseProvidedController : XBaseIdentityApiV1Controller, IXBaseProvidedController + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + private readonly IXBaseProvided provider; + private readonly Func, IOrderedQueryable> defaultOrderBuilder; + private readonly Func, IIncludableQueryable> defaultIncludeBuilder; + + protected XBaseProvidedController( + ILogger> logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXBaseProvided provider, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { + this.provider = provider; + this.defaultOrderBuilder = defaultOrderBuilder; + this.defaultIncludeBuilder = defaultIncludeBuilder; + } + + // + #region Retrieve ... + /// + /// Get Specified Item by ID ... + /// + /// + /// + /// + /// + [HttpGet("{id}")] + public virtual async Task> Get( + [FromRoute] TKey id, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(id); + + // + var result = await provider.Get( + id: id, + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get All Exists Items ... + /// + /// + /// + /// + /// + [HttpGet("")] + public virtual async Task>> GetAll( + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await provider.GetAll( + orderBuilder: !useDefaultOrders + ? null + : defaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find an item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + [HttpGet("Find/One")] + public virtual async Task> FindOne( + [FromRoute] string query, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(query) + .ValidateGroupAsync(); + + // + var result = await provider.FindOne( + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken, + predicate: x => x.PropValuesContains(query) + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find a collection of Items by proving a Query Search ... + /// + /// + /// + /// + /// + /// + [HttpGet("Find/Many")] + public virtual async Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(query) + .ValidateGroupAsync(); + + // + var result = await provider.FindMany( + orderBuilder: !useDefaultOrders + ? null + : defaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken, + predicate: x => x.PropValuesContains(query) + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + public virtual async Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(query) + .ValidateGroupAsync(); + + // + var result = await provider.Query( + query: query, + predicate: null, + orderBuilder: !useDefaultOrders + ? null + : defaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Owned Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + [HttpGet("Query/Owned")] + public virtual async Task>> QueryOwned( + [FromQuery] XQuery query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(query) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + + // + var result = await provider.QueryOwned( + query: query, + predicate: null, + userInfo: userInfo, + orderBuilder: !useDefaultOrders + ? null + : defaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Add/Update/Remove ... + /// + /// Add an item ... + /// + /// + /// + /// + [HttpPost("")] + public virtual async Task> Add( + [FromBody] TDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(item) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Add( + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update an item values ... + /// + /// + /// + /// + /// + [HttpPut("{id}")] + public virtual async Task> Update( + [FromRoute] TKey id, + [FromBody] TDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotNull(item) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Update( + id: id, + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove an Item ... + /// + /// + /// + /// + [HttpDelete("{id}")] + public virtual async Task> Remove( + [FromRoute] TKey id, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Remove( + id: id, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Controllers\XBaseRepositoryProviderController.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xDataService.Extensions; +using xExceptions.Constants; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; +using xPushService.Interfaces; + +namespace xPushService.Controllers +{ + [AllowAnonymous] + public abstract class XBaseRepositoryProviderController : XBaseIdentityApiV1Controller, IXBaseRepositoryProviderController + where TEntity : XBaseEntity + where THub : XBaseEntityHub + { + // + #region Properties ... + public IXBaseEntityProvider Provider { get; } + public Func, IOrderedQueryable> DefaultOrderBuilder { get; } + public Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + #endregion + + protected XBaseRepositoryProviderController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXBaseEntityProvider provider, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger: logger, + appConfiguration: appConfiguration, + identityProvider: identityProvider, + validationProvider: validationProvider + ) + { + // + Provider = provider; + DefaultOrderBuilder = defaultOrderBuilder; + DefaultIncludeBuilder = defaultIncludeBuilder; + } + + // + #region Actions ... + /// + /// add a new Item ... + /// + /// + /// + /// + /// + [HttpPost] + public virtual async Task> Add( + [FromBody] TEntity item, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + var connectionId = GetConnectionId(); + + // + var result = await Provider.AddAsync( + item: item, + saveChanges: saveChanges, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update an Item values ... + /// + /// + /// + /// + /// + /// + [HttpPut("{id}")] + public virtual async Task> Update( + [FromRoute] TKey id, + [FromBody] TEntity item, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + var connectionId = GetConnectionId(); + + // + var result = await Provider.UpdateAsync( + id: id, + item: item, + saveChanges: saveChanges, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// remove an Item by it's Id ... + /// + /// + /// + /// + /// + /// + [HttpDelete("{id}")] + public virtual async Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var connectionId = GetConnectionId(); + + // + var result = await Provider.RemoveAsync( + id: id, + softDelete: softDelete, + saveChanges: saveChanges, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + /// + [HttpGet("{id}/[action]")] + public virtual async Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await Provider.IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// count all exists Items ... + /// + /// + /// + /// + [HttpGet("Count")] + public virtual async Task> Count( + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await Provider.CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// retrieve an Item by it's Id ... + /// + /// + /// + /// + /// + /// + [HttpGet("{id}")] + public virtual async Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await Provider.GetAsync( + id: id, + includeBuilder: !useDefaultIncludes + ? null + : DefaultIncludeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// retrieve all exists Items ... + /// + /// + /// + /// + /// + /// + [HttpGet] + public virtual async Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await Provider.GetAllAsync( + orderBuilder: !useDefaultOrders + ? null + : DefaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : DefaultIncludeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// find an Item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + [HttpGet("FindOne")] + public virtual async Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotEmpty(query); + + // + Expression> predicate = x => x.PropValuesContains(query); + var result = await Provider.FindOneAsync( + predicate: predicate, + includeBuilder: !useDefaultIncludes + ? null + : DefaultIncludeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// find a collection of Items by proving a Query Search ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindMany")] + public virtual async Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotEmpty(query); + + // + Expression> predicate = x => x.PropValuesContains(query); + var result = await Provider.FindManyAsync( + predicate: predicate, + orderBuilder: !useDefaultOrders + ? null + : DefaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : DefaultIncludeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + public virtual async Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + if (query.IsNullOrDefault()) + { + query = query.NormalizeQuery(Provider.DataConfiguration); + } + + // + var result = await Provider.QueryAsync( + query: query, + predicate: null, + orderBuilder: !useDefaultOrders + ? null + : DefaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : DefaultIncludeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region NonActions ... + /// + /// Send Custom Push Message ... + /// + /// + /// + /// + /// + /// + [NonAction] + public virtual async Task SendPush( + string action, + string payLoad, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + await Provider.SendPush( + action: action, + payLoad: payLoad, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Controllers\XBaseServiceProviderController.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xDataService.Extensions; +using xExceptions.Constants; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; +using xPushService.Interfaces; + +namespace xPushService.Controllers +{ + [AllowAnonymous] + public abstract class XBaseServiceProviderController : XBaseIdentityApiV1Controller, IXBaseServiceProviderController + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + // + #region Properties ... + public IXBaseDtoProvider Provider { get; } + public Func, IOrderedQueryable> DefaultOrderBuilder { get; } + public Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + #endregion + + // + #region Constructor ... + public XBaseServiceProviderController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXBaseDtoProvider provider, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { + // + Provider = provider; + DefaultOrderBuilder = defaultOrderBuilder; + DefaultIncludeBuilder = defaultIncludeBuilder; + } + #endregion + + // + #region Actions ... + /// + /// add a new Item ... + /// + /// + /// + /// + /// + [HttpPost] + public virtual async Task> Add( + [FromBody] TDto item, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + var connectionId = GetConnectionId(); + + // + var result = await Provider.AddAsync( + item: item, + saveChanges: saveChanges, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update an Item values ... + /// + /// + /// + /// + /// + /// + [HttpPut("{id}")] + public virtual async Task> Update( + [FromRoute] TKey id, + [FromBody] TDto item, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + var connectionId = GetConnectionId(); + + // + var result = await Provider.UpdateAsync( + id: id, + item: item, + saveChanges: saveChanges, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// remove an Item by it's Id ... + /// + /// + /// + /// + /// + /// + [HttpDelete("{id}")] + public virtual async Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var connectionId = GetConnectionId(); + + // + var result = await Provider.RemoveAsync( + id: id, + softDelete: softDelete, + saveChanges: saveChanges, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + /// + [HttpGet("{id}/[action]")] + public virtual async Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await Provider.IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// count all exists Items ... + /// + /// + /// + /// + [HttpGet("Count")] + public virtual async Task> Count( + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await Provider.CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// retrieve an Item by it's Id ... + /// + /// + /// + /// + /// + /// + [HttpGet("{id}")] + public virtual async Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await Provider.GetAsync( + id: id, + includeBuilder: !useDefaultIncludes + ? null + : DefaultIncludeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// retrieve all exists Items ... + /// + /// + /// + /// + /// + /// + [HttpGet] + public virtual async Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await Provider.GetAllAsync( + orderBuilder: !useDefaultOrders + ? null + : DefaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : DefaultIncludeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// find an Item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + [HttpGet("FindOne")] + public virtual async Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotEmpty(query); + + // + Expression> predicate = x => x.PropValuesContains(query); + var result = await Provider.FindOneAsync( + predicate: predicate, + includeBuilder: !useDefaultIncludes + ? null + : DefaultIncludeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// find a collection of Items by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindMany")] + public virtual async Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotEmpty(query); + + // + Expression> predicate = x => x.PropValuesContains(query); + var result = await Provider.FindManyAsync( + predicate: predicate, + orderBuilder: !useDefaultOrders + ? null + : DefaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : DefaultIncludeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + public virtual async Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + if (query.IsNullOrDefault()) + { + query = query.NormalizeQuery(Provider.DataConfiguration); + } + + // + var result = await Provider.QueryAsync( + query: query, + predicate: null, + orderBuilder: !useDefaultOrders + ? null + : DefaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : DefaultIncludeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region NonActions ... + /// + /// Send Custom Push Message ... + /// + /// + /// + /// + /// + /// + [NonAction] + public virtual async Task SendPush( + string action, + string payLoad, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + await Provider.SendPush( + action: action, + payLoad: payLoad, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\DI\XDIHelperExtension.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using xCommons.Extensions; +using xExceptions.Constants; +using xPushService.Configurations; +using xPushService.Constants; +using xPushService.Helpers; +using xPushService.Providers; + +namespace xPushService.DI +{ + public static partial class XDIHelperExtension + { + /// + /// Extract Module Configuration from provided IConfiguration + /// + /// + /// an instance of XPushServiceConfiguration + public static XPushServiceConfiguration GetXPushServiceConfiguration(this IConfiguration config) + { + // + var xPushConfigSection = config.GetSection(XPushServiceConstants.XConfigurationNodes.XPushServiceConfiguration); + return xPushConfigSection.Get(); + } + + /// + /// Register XPushServiceHelper ... + /// + /// + /// + public static void AddXPushServiceHelper( + this IServiceCollection services, + XPushServiceHelper helper + ) + { + // + // Validate Args ... + if (helper.IsNull()) + { + // + Log("AddXPushServiceHelper failed, helper not provided ..."); + throw XException.InvalidArgs.ToException(); + } + + // + // Register Helper ... + services.AddSingleton(helper); + } + + /// + /// Register Module Provided Service on DI + /// + /// + /// + /// + public static void AddXPushService( + this IServiceCollection services, + IConfiguration config, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + AddPushService( + services, + config.GetXPushServiceConfiguration(), + lifeTime + ); + } + + /// + /// Register Module Provided Service on DI + /// + /// + /// + /// + public static void AddXPushService( + this IServiceCollection services, + XPushServiceConfiguration config, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + AddPushService( + services, + config, + lifeTime + ); + } + + /// + /// Use Module Middlewares on Application Builder + /// + /// + /// + public static void UseXPushService( + this IApplicationBuilder app, + XPushServiceHelper helper + ) + { + // + // Validate Args ... + var isHelperExists = !helper.IsNull(); + if (!isHelperExists) + { + // + Log("AddXPushServiceHelper failed, helper not provided ..."); + throw XException.InvalidArgs.ToException(); + } + + // + // Create an Scope for Accessing Registered Services ... + using (var scope = app.ApplicationServices.CreateScope()) + { + // + // try to Retrieve Configuration .... + var config = scope.ServiceProvider.GetService(); + if ( + config.IsNull() || + config.BaseRoute.IsNullOrEmpty() || + config.BaseRoute.Trim().IsNullOrEmpty() + ) + { + // + Log("AddXPushServiceHelper failed, Configuration not provided ..."); + throw XException.InvalidConfiguration.ToException(); + } + + // + #region Normalize Routes ... + // + // Base Reoute ... + config.BaseRoute = config.BaseRoute.ToNormalString(); + if (!config.BaseRoute.StartsWith("/")) + { + config.BaseRoute = "/" + config.BaseRoute; + } + #endregion + + // + // add XPushService Hub to Helper ... + // + var hubRoutes = new List(); + + // + // Try to Register Hubs ... + // + // Use SignalR in App ... + app.UseSignalR(routes => + { + // + helper.GetHubs().ForEach(hubDescriptor => + { + // + var hubRoute = config.BaseRoute + "/" + hubDescriptor.Route; + hubRoutes.Add(hubRoute); + + // + try + { + // + Type routesType = routes.GetType(); + MethodInfo mapHubMethod = routesType + .GetMethods() + .SingleOrDefault(m => + m.Name == "MapHub" && + m.GetParameters().Length == 1 + ); + if (mapHubMethod.IsNull()) + { + throw XException.ActionFailed.ToException(); + } + + // + object[] genericMapHubMethodArgs = { new PathString(hubRoute) }; + MethodInfo genericMapHubMethod = mapHubMethod.MakeGenericMethod(hubDescriptor.Hub); + genericMapHubMethod.Invoke(routes, genericMapHubMethodArgs); + + // + Log($"Register {hubDescriptor.Hub} Hub: {hubRoute}"); + } + catch + { + throw; + } + }); + }); + } + } + + // + #region Private ... + /// + /// a LogTag for XPushService ... + /// + private static string XLogTag = "XPushService"; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Push Service ... + /// + /// + /// + /// + private static void AddPushService( + IServiceCollection services, + XPushServiceConfiguration config, + ServiceLifetime lifeTime + ) + { + // + #region Validate Args ... + // + var exception = XException.InvalidArgs.ToException(); ; + + // + // Services ... + var isServicesExists = !services.IsNull(); + if (!isServicesExists) + { + // + Log("AddXPushServiceHelper failed, services not provided ..."); + throw exception; + } + + // + // Config ... + var isConfigExists = !config.IsNull(); + if (!isConfigExists) + { + // + Log("AddXPushServiceHelper failed, configuration not provided ..."); + throw exception; + } + + // + // Lifetime ... + var isLifetimeExists = !lifeTime.IsNull(); + if (!isLifetimeExists) + { + // + Log("AddXPushServiceHelper failed, lifetime not provided ..."); + throw exception; + } + #endregion + + // + // Register XPushService Configuration ... + services.AddSingleton(config); + + // + // Register SignalR ... + var builder = services.AddSignalR(options => + { + options.KeepAliveInterval = TimeSpan.FromSeconds(10); + options.ClientTimeoutInterval = TimeSpan.FromSeconds(10); + }); + + // + // Add NewtonSoftJson Protocol ... + builder.AddNewtonsoftJsonProtocol(x => + { + x.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + x.PayloadSerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + }); + + // + // Register XPushServiceUserNameProvider ... + services.AddSingleton(); + + // + // add Message Protocol Support ... + if (config.AddSupportMessageProtocol) + { + builder.AddMessagePackProtocol(); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Extensions\ClaimsPrincipalExtensions.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.SignalR; +using xCommons.Constants; +using xCommons.Extensions; +using xCommons.Helpers; +using xIdentityModels.Constants; +using xPushService.Models; + +namespace xPushService.Extensions +{ + public static class ClaimsPrincipalExtensions + { + /// + /// Generate User Info based on HubCallerContext ... + /// + /// + /// + public static XHubUserInfo GetUserInfo(this HubCallerContext source) + { + // + XHubUserInfo result = null; + + // + if (source.IsNull() || source.User.IsNull() || !source.User.Claims.HasChild()) + { + return result; + } + + // + result = new XHubUserInfo + { + UserId = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.UserId)?.Value ?? "", + UserName = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.UserName)?.Value ?? "", + FirstName = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.FirstName)?.Value ?? "", + LastName = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.LastName)?.Value ?? "", + Picture = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.Picture)?.Value ?? "", + Email = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.Email)?.Value ?? null, + PhoneNumber = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.PhoneNumber)?.Value ?? null, + }; + + // + #region Handle Gender ... + var genders = ObjectHelper.ToEnumerableKeys(); + var genderStr = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.Gender).Value ?? null; + if (genderStr.IsNullOrEmpty()) + { + result.Gender = XGender.Male; + } + else + { + result.Gender = genderStr.GetValue(); + } + #endregion + + // + #region Handle IsBanned ... + var isBannedStr = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.IsBanned).Value ?? null; + if (isBannedStr.IsNullOrEmpty()) + { + result.IsBanned = false; + } + else + { + // + var isBanned = false; + Boolean.TryParse(isBannedStr, out isBanned); + + // + result.IsBanned = isBanned; + } + #endregion + + // + #region Handle IsEnabled ... + var isEnabledStr = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.IsEnabled).Value ?? null; + if (isEnabledStr.IsNullOrEmpty()) + { + result.IsEnabled = false; + } + else + { + // + var isEnabled = false; + Boolean.TryParse(isEnabledStr, out isEnabled); + + // + result.IsEnabled = isEnabled; + } + #endregion + + // + #region Handle Email Confirmed ... + var emailConfirmedStr = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.EmailVerified).Value ?? null; + if (emailConfirmedStr.IsNullOrEmpty()) + { + result.EmailConfirmed = false; + } + else + { + // + var emailConfirmed = false; + Boolean.TryParse(emailConfirmedStr, out emailConfirmed); + + // + result.EmailConfirmed = emailConfirmed; + } + #endregion + + // + #region Handle PhoneNumber Confirmed ... + var phoneNumberConfirmedStr = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.PhoneNumberVerified).Value ?? null; + if (phoneNumberConfirmedStr.IsNullOrEmpty()) + { + result.PhoneNumberConfirmed = false; + } + else + { + // + var phoneNumberConfirmed = false; + Boolean.TryParse(phoneNumberConfirmedStr, out phoneNumberConfirmed); + + // + result.PhoneNumberConfirmed = phoneNumberConfirmed; + } + #endregion + + // + #region Handle ExpiredOn ... + var expiredOnStr = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.ExpiredOn).Value ?? null; + if (expiredOnStr.IsNullOrEmpty()) + { + result.ExpiredOn = null; + } + else + { + // + long expiredOn = 0; + long.TryParse(expiredOnStr, out expiredOn); + + // + result.ExpiredOn = expiredOn; + } + #endregion + + // + #region Handle AuthenticatedOn ... + var authenticatedOnStr = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.AuthenticatedOn).Value ?? null; + if (authenticatedOnStr.IsNullOrEmpty()) + { + result.AuthenticatedOn = null; + } + else + { + // + long authenticatedOn = 0; + long.TryParse(authenticatedOnStr, out authenticatedOn); + + // + result.AuthenticatedOn = authenticatedOn; + } + #endregion + + // + #region Handle BrithDate ... + var brithDateStr = source.User.Claims.FirstOrDefault(c => c.Type == XCustomClaims.BrithDate)?.Value ?? ""; + if (brithDateStr.IsNullOrEmpty()) + { + // BrithDate = null; + } + else + { + // + DateTime bDate; + DateTime.TryParse(brithDateStr, out bDate); + + // + result.BrithDate = bDate; + } + #endregion + + // + result.Issuers = source.User.Claims.Where(c => c.Type == XCustomClaims.Issuer)? + .Select(c => c.Value) ?? new HashSet(); + result.Audiences = source.User.Claims.Where(c => c.Type == XCustomClaims.Audience)? + .Select(c => c.Value) ?? new HashSet(); + result.ClientIds = source.User.Claims.Where(c => c.Type == XCustomClaims.ClientId)? + .Select(c => c.Value) ?? new HashSet(); + result.Scopes = source.User.Claims.Where(c => c.Type == XCustomClaims.Scope)? + .Select(c => c.Value) ?? new HashSet(); + result.Roles = source.User.Claims.Where(c => c.Type == XCustomClaims.Role)? + .Select(c => c.Value) ?? new HashSet(); + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Helpers\XPushServiceHelper.cs + +using System.Collections.Generic; +using System.Linq; +using xCommons.Extensions; +using xExceptions.Constants; +using xPushService.Base; +using xPushService.Models; + +namespace xPushService.Helpers { + public class XPushServiceHelper { + /// + /// a collection of Hubs for Registring ... + /// + /// + private List hubs = new List (); + + /// + /// add specified hub to provided hubs for registration ... + /// + /// + /// + public void AddHub (string route) where TXHub : XBaseHub { + // + #region Validate Args ... + // + // check route not null ... + if ( + route.IsNullOrEmpty () || + route.Trim ().IsNullOrEmpty () + ) { + throw XException.InvalidArgs.ToException (); + } + + // + // Normalize Route ... + route = route.ToNormalString (); + + // + // check route and hub must be unique ... + var isRouteExists = hubs.Any (hd => hd.Route == route); + var isHubExists = hubs.Any (hd => hd.Hub == typeof (TXHub)); + if ( + isHubExists || + isRouteExists + ) { + throw XException.Duplicate.ToException (); + } + #endregion + + // + // Add Provided Hub to List ... + hubs.Add (new XHubDescriptor { + Route = route, + Hub = typeof (TXHub) + }); + } + + /// + /// remove an specific hub from provided list ... + /// + /// + public void RemoveHub () where TXHub : XBaseHub { + // + var existsHubDescriptor = hubs.SingleOrDefault (hd => hd.Hub == typeof (TXHub)); + if (!existsHubDescriptor.IsNull ()) { + hubs.Remove (existsHubDescriptor); + } + } + + /// + /// retrieve provided hubs count ... + /// + /// + public int Count () { + return hubs.Count; + } + + /// + /// retrieve all provided hubs for registration ... + /// + /// + public List GetHubs () { + return hubs; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseDtoHub.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xModels.Base; + +namespace xPushService.Interfaces +{ + public interface IXBaseDtoHub + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + /// + /// Notify a New Item added ... + /// + /// + /// + /// + Task Add( + TDto item, + CancellationToken cancellationToken = default + ); + + /// + /// Notify a New Item added ... + /// + /// + /// + /// + Task AddOrUpdate( + TDto item, + CancellationToken cancellationToken = default + ); + + /// + /// Notify Add Many Items ... + /// + /// + /// + /// + Task AddMany( + IEnumerable items, + CancellationToken cancellationToken = default + ); + + /// + /// Notify an Item Deleted ... + /// + /// + /// + /// + Task Delete( + TDto item, + CancellationToken cancellationToken = default + ); + + /// + /// Notify Delete Many Items ... + /// + /// + /// + /// + Task DeleteMany( + IEnumerable items, + CancellationToken cancellationToken = default + ); + + /// + /// Notify an Item Updated ... + /// + /// + /// + /// + Task Update( + TDto item, + CancellationToken cancellationToken = default + ); + + /// + /// Notify Update Many Items ... + /// + /// + /// + /// + Task UpdateMany( + IEnumerable items, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseDtoProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore.Query; +using xDataService.Configuration; +using xDataService.Interfaces; +using xIdentityService.Interfaces; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; + +namespace xPushService.Interfaces +{ + public interface IXBaseDtoProvider + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + // + #region Props ... + IHubContext Hub { get; } + IXIdentityProvider IdentityProvider { get; } + XDataServiceConfiguration DataConfiguration { get; } + IXBaseRepositoryService Service { get; } + #endregion + + // + #region Actions ... + /// + /// add a new Item ... + /// + /// + /// + /// + /// + /// + Task AddAsync( + TDto item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Item values ... + /// + /// + /// + /// + /// + /// + /// + Task UpdateAsync( + TKey id, + TDto item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// remove an Item by it's Id ... + /// + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// count all exists Items ... + /// + /// + /// + /// + Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + /// + Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve an Item by it's Id ... + /// + /// + /// + /// + /// + /// + Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Items ... + /// + /// + /// + /// + /// + /// + Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Dtos + /// as Async Enumerable ... + /// + /// + /// + /// + /// + /// + IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find an Item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find a collection of Items by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Hub Actions ... + /// + /// Send Custom Push Message ... + /// + /// + /// + /// + /// + /// + Task SendPush( + string action, + string payLoad, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseEntityHub.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xModels.Base; + +namespace xPushService.Interfaces +{ + public interface IXBaseEntityHub + where TEntity : XBaseEntity + { + /// + /// Notify a New Entity added ... + /// + /// + /// + /// + Task Add( + TEntity entity, + CancellationToken cancellationToken = default + ); + + /// + /// Notify a New Entity added ... + /// + /// + /// + /// + Task AddOrUpdate( + TEntity entity, + CancellationToken cancellationToken = default + ); + + /// + /// Notify Add Many Entities ... + /// + /// + /// + /// + Task AddMany( + IEnumerable entities, + CancellationToken cancellationToken = default + ); + + /// + /// Notify an Entity Deleted ... + /// + /// + /// + /// + Task Delete( + TEntity entity, + CancellationToken cancellationToken = default + ); + + /// + /// Notify Delete Many Entities ... + /// + /// + /// + /// + Task DeleteMany( + IEnumerable entities, + CancellationToken cancellationToken = default + ); + + /// + /// Notify an Entity Updated ... + /// + /// + /// + /// + Task Update( + TEntity entity, + CancellationToken cancellationToken = default + ); + + /// + /// Notify Update Many Entities ... + /// + /// + /// + /// + Task UpdateMany( + IEnumerable entities, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseEntityProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore.Query; +using xDataService.Configuration; +using xDataService.Interfaces; +using xIdentityService.Interfaces; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; + +namespace xPushService.Interfaces +{ + /// + /// a Provider is a way to Manipulate Data using Repository or Services and Contains Push + /// notifications ability ... + /// + /// + /// + public interface IXBaseEntityProvider + where TEntity : XBaseEntity + where THub : XBaseEntityHub + { + // + #region Props ... + IHubContext Hub { get; } + IXIdentityProvider IdentityProvider { get; } + IXBaseRepository Repository { get; } + XDataServiceConfiguration DataConfiguration { get; } + #endregion + + // + #region Actions ... + /// + /// add a new Item ... + /// + /// + /// + /// + /// + /// + Task AddAsync( + TEntity item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Item values ... + /// + /// + /// + /// + /// + /// + /// + Task UpdateAsync( + TKey id, + TEntity item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// remove an Item by it's Id ... + /// + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// count all exists Items ... + /// + /// + /// + /// + Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + /// + Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve an Item by it's Id ... + /// + /// + /// + /// + /// + /// + Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Items ... + /// + /// + /// + /// + /// + /// + Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find an Item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find a collection of Items by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Hub Actions ... + /// + /// Send Custom Push Message ... + /// + /// + /// + /// + /// + /// + Task SendPush( + string action, + string payLoad, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseHub.cs + +using System.Threading; +using System.Threading.Tasks; + +namespace xPushService.Interfaces +{ + public interface IXBaseHub + { + Task SendCustomAction( + string actionName, + string payLoad, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseProvided.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Query; +using xIdentityModels.Models; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; + +namespace xPushService.Interfaces +{ + /// + /// a Provided Service Used for Providing Data Based on Validations of Users ... + /// + /// + /// + /// + /// + public interface IXBaseProvided + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + /// + /// Get Specified Item by ID ... + /// + /// + /// + /// + /// + Task Get( + TKey id, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Exists Items ... + /// + /// + /// + /// + /// + Task> GetAll( + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Find an item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + Task FindOne( + Expression> predicate, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Find a collection of Items by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + Task> FindMany( + Expression> predicate, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + Task> Query( + XQuery query, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Owned Items based on XQuery Pagination structure ... + /// if supported ... + /// + /// + /// + /// + /// + /// + /// + /// + Task> QueryOwned( + XQuery query, + XUserClaimsInfoDto userInfo = null, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add an item values ... + /// + /// + /// + /// + /// + /// + Task Add( + TDto item, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update an item values ... + /// + /// + /// + /// + /// + /// + /// + Task Update( + TKey id, + TDto item, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// remove an Item ... + /// + /// + /// + /// + /// + /// + Task Remove( + TKey id, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// count all exists Items ... + /// + /// + /// + Task Count( + CancellationToken cancellationToken = default + ); + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + Task IsExists( + TKey id, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseProvidedController.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; + +namespace xPushService.Interfaces +{ + /// + /// a Provided Controller Used for Providing a Provided Service Actions ... + /// + /// + /// + /// + /// + public interface IXBaseProvidedController + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + // + #region Retrieve ... + /// + /// Get Specified Item by ID ... + /// + /// + /// + /// + /// + [HttpGet("{id}")] + Task> Get( + [FromRoute] TKey id, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// Get All Exists Items ... + /// + /// + /// + /// + /// + [HttpGet("")] + Task>> GetAll( + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// Find an item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + [HttpGet("Find/One")] + Task> FindOne( + [FromQuery] string query, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// Find a collection of Items by proving a Query Search ... + /// + /// + /// + /// + /// + /// + [HttpGet("Find/Many")] + Task>> FindMany( + [FromQuery] string query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Owned Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + [HttpGet("Query/Owned")] + Task>> QueryOwned( + [FromQuery] XQuery query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Add/Update/Remove ... + /// + /// Add an item ... + /// + /// + /// + /// + [HttpPost("")] + Task> Add( + [FromBody] TDto item, + CancellationToken cancellationToken = default + ); + + /// + /// Update an item values ... + /// + /// + /// + /// + /// + [HttpPut("{id}")] + Task> Update( + [FromRoute] TKey id, + [FromBody] TDto item, + CancellationToken cancellationToken = default + ); + + /// + /// Remove an Item ... + /// + /// + /// + /// + [HttpDelete("{id}")] + Task> Remove( + [FromRoute] TKey id, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore.Query; +using xDataService.Configuration; +using xDataService.Interfaces; +using xIdentityService.Interfaces; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; + +namespace xPushService.Interfaces +{ + /// + /// a Provider is a way to Manipulate Data using Repository or Services and Contains Push + /// notifications ability ... + /// + /// + /// + /// + public interface IXBaseProvider + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + #region Props ... + // + IXIdentityProvider IdentityProvider { get; } + XDataServiceConfiguration DataConfiguration { get; } + IHubContext> Hub { get; } + IXBaseRepositoryService RepositoryService { get; } + #endregion + + // + #region Actions ... + /// + /// add a new Dto ... + /// + /// + /// + /// + /// + /// + Task AddAsync( + TDto item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Dto values ... + /// + /// + /// + /// + /// + /// + /// + Task UpdateAsync( + TKey id, + TDto item, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// remove an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// count all exists Dtos ... + /// + /// + /// + /// + Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// Check a Dto exists or not ... + /// + /// + /// + /// + /// + Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Dtos ... + /// + /// + /// + /// + /// + /// + Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find an Dto by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find a collection of Dtos by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve Dtos based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Hub Actions ... + /// + /// Send Custom Push Message ... + /// + /// + /// + /// + /// + /// + Task SendPush( + string action, + string payLoad, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseRepositoryProviderController.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; + +namespace xPushService.Interfaces +{ + public interface IXBaseRepositoryProviderController + where TEntity : XBaseEntity + where THub : XBaseEntityHub + { + // + #region Properties ... + IXBaseEntityProvider Provider { get; } + Func, IOrderedQueryable> DefaultOrderBuilder { get; } + Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + #endregion + + // + #region Actions ... + /// + /// add a new Item ... + /// + /// + /// + /// + /// + [HttpPost] + Task> Add( + [FromBody] TEntity item, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Item values ... + /// + /// + /// + /// + /// + /// + [HttpPut("{id}")] + Task> Update( + [FromRoute] TKey id, + [FromBody] TEntity item, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// remove an Item by it's Id ... + /// + /// + /// + /// + /// + /// + [HttpDelete("{id}")] + Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + /// + [HttpGet("{id}/[action]")] + Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// count all exists Items ... + /// + /// + /// + /// + [HttpGet("Count")] + Task> Count( + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve an Item by it's Id ... + /// + /// + /// + /// + /// + /// + [HttpGet("{id}")] + Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Items ... + /// + /// + /// + /// + /// + /// + [HttpGet] + Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// find an Item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + [HttpGet("FindOne")] + Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// find a collection of Items by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindMany")] + Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Non Actions ... + /// + /// Send Custom Push Message ... + /// + /// + /// + /// + /// + /// + [NonAction] + Task SendPush( + string action, + string payLoad, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseServiceProviderController.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; + +namespace xPushService.Interfaces +{ + public interface IXBaseServiceProviderController + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + // + #region Properties ... + IXBaseDtoProvider Provider { get; } + Func, IOrderedQueryable> DefaultOrderBuilder { get; } + Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + #endregion + + // + #region Actions ... + /// + /// add a new Item ... + /// + /// + /// + /// + /// + [HttpPost] + Task> Add( + [FromBody] TDto item, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// Update an Item values ... + /// + /// + /// + /// + /// + /// + [HttpPut("{id}")] + Task> Update( + [FromRoute] TKey id, + [FromBody] TDto item, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// remove an Item by it's Id ... + /// + /// + /// + /// + /// + /// + [HttpDelete("{id}")] + Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + [FromQuery] bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + /// + [HttpGet("{id}/[action]")] + Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// count all exists Items ... + /// + /// + /// + /// + [HttpGet("Count")] + Task> Count( + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve an Item by it's Id ... + /// + /// + /// + /// + /// + /// + [HttpGet("{id}")] + Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Items ... + /// + /// + /// + /// + /// + /// + [HttpGet] + Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// find an Item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + [HttpGet("FindOne")] + Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// find a collection of Items by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindMany")] + Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Non Actions ... + /// + /// Send Custom Push Message ... + /// + /// + /// + /// + /// + /// + [NonAction] + Task SendPush( + string action, + string payLoad, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXBaseWebRTCHub.cs + +using System.Threading; +using System.Threading.Tasks; + +namespace xPushService.Interfaces +{ + public interface IXBaseWebRTCHub : IXBaseHub + { + /// + /// Sending WebRTC Offer To Specified Connection ... + /// + /// + /// + /// + /// + Task Offer( + string offer, + string to, + CancellationToken cancellationToken = default + ); + + /// + /// Sending WebRTC Answer to Specified Connection ... + /// + /// + /// + /// + Task Answer( + string answer, + CancellationToken cancellationToken = default + ); + + /// + /// Sending WebRTC Candidate to Specified Connecton ... + /// + /// + /// + /// + /// + Task Candidate( + string candidate, + string to, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Interfaces\IXHubContextFactory.cs + +using Microsoft.AspNetCore.SignalR; + +namespace xPushService.Interfaces +{ + public interface IXHubContextFactory + { + IHubContext Create() where THub : Hub; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Models\XHubConnectionDto.cs + +using xModels.Base; + +namespace xPushService.Models +{ + public class XHubConnectionDto : XBaseDto + { + public string ConnectionId { get; set; } + public string UserId { get; set; } + public string Username { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Models\XHubDescriptor.cs + +using System; + +namespace xPushService.Models { + /// + /// Describe a Hub to Register ... + /// + public class XHubDescriptor { + /// + /// Specified the Hub Route ... + /// + /// + public string Route { get; set; } + + /// + /// Specified the Hub Class Type ... + /// + /// + public Type Hub { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Models\XHubUserInfo.cs + +using xModels.Base; +using xIdentityModels.Constants; +using System; +using System.Collections.Generic; + +namespace xPushService.Models +{ + public class XHubUserInfo : XBaseDto + { + // + #region Properties ... + public string UserId { get; set; } + public string UserName { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Picture { get; set; } + public string Email { get; set; } + public string PhoneNumber { get; set; } + public XGender Gender { get; set; } + public bool IsBanned { get; set; } + public bool IsEnabled { get; set; } + public bool EmailConfirmed { get; set; } + public bool PhoneNumberConfirmed { get; set; } + public long? ExpiredOn { get; set; } + public long? AuthenticatedOn { get; set; } + public DateTime? BrithDate { get; set; } + public IEnumerable Issuers { get; set; } = new HashSet(); + public IEnumerable Audiences { get; set; } = new HashSet(); + public IEnumerable ClientIds { get; set; } = new HashSet(); + public IEnumerable Scopes { get; set; } = new HashSet(); + public IEnumerable Roles { get; set; } = new HashSet(); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Providers\XBaseProvided.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Query; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; +using xPushService.Interfaces; + +namespace xPushService.Providers +{ + /// + /// a Provided Service Used for Providing Data Based on Validations of Users ... + /// + /// + /// + /// + /// + public abstract class XBaseProvided : IXBaseProvided + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + // + #region Props ... + private readonly string[] permittedRoles; + private readonly IXBaseDtoProvider provider; + #endregion + + // + #region Constructor ... + protected XBaseProvided( + IXBaseDtoProvider provider, + string[] permittedRoles = null + ) + { + this.provider = provider; + this.permittedRoles = permittedRoles; + } + #endregion + + /// + /// Check an Items is Owned for User or not ... + /// + /// + /// + /// + public abstract bool IsOwned( + TDto item, + XUserClaimsInfoDto userInfo + ); + public abstract bool IsOwned( + TEntity item, + XUserClaimsInfoDto userInfo + ); + + /// + /// Check User Has Permission for Manipulate Data or not ... + /// + /// + /// + public bool HasPermision( + XUserClaimsInfoDto userInfo + ) + { + // + var result = + !permittedRoles.HasChild() || + (!userInfo.IsNullOrDefault() && + userInfo.HasAccess(permittedRoles)); + + // + return result; + } + + // + #region Provided Actions ... + /// + /// Get Specified Item by ID ... + /// + /// + /// + /// + /// + public virtual async Task Get( + TKey id, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Get All Exists Items ... + /// + /// + /// + /// + /// + public virtual async Task> GetAll( + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.GetAllAsync( + ignoreSoftDeleteds: true, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// find an item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + public virtual async Task FindOne( + Expression> predicate, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.FindOneAsync( + predicate: predicate, + ignoreSoftDeleteds: true, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// find a collection of Items by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public virtual async Task> FindMany( + Expression> predicate, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.FindManyAsync( + predicate: predicate, + ignoreSoftDeleteds: true, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task> Query( + XQuery query, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.QueryAsync( + query: query, + predicate: predicate, + ignoreSoftDeleteds: true, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// retrieve Owned Items based on XQuery Pagination structure ... + /// if supported ... + /// + /// + /// + /// + /// + /// + /// + /// + public virtual async Task> QueryOwned( + XQuery query, + XUserClaimsInfoDto userInfo = null, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + Expression> predicateor = null; + if (!predicate.IsNull()) + { + // + var predicateFnc = predicate.Compile(); + predicateor = x => + predicateFnc(x) && + IsOwned(x, userInfo); + } + else + { + predicateor = x => IsOwned(x, userInfo); + } + + // + var result = await provider.QueryAsync( + query: query, + predicate: predicateor, + ignoreSoftDeleteds: true, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Add an item values ... + /// + /// + /// + /// + /// + /// + public virtual async Task Add( + TDto item, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.AddAsync( + item: item, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Update an item values ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task Update( + TKey id, + TDto item, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Check Permission ... + var hasPermission = + IsOwned(item, userInfo) || + HasPermision(userInfo); + if (!hasPermission) + { + XException.NotAllowed.Throw(); + } + + // + var result = await provider.UpdateAsync( + id: id, + item: item, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// remove an Item ... + /// + /// + /// + /// + /// + /// + public virtual async Task Remove( + TKey id, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Check Permission ... + var item = await Get( + id: id, + cancellationToken: cancellationToken + ); + var hasPermission = + IsOwned(item, userInfo) || + HasPermision(userInfo); + if (!hasPermission) + { + XException.NotAllowed.Throw(); + } + + // + var result = await provider.RemoveAsync( + id: id, + saveChanges: true, + softDelete: false, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// count all exists Items ... + /// + /// + /// + public virtual async Task Count( + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.CountAsync( + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + public virtual async Task IsExists( + TKey id, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.IsExistsAsync( + id: id, + cancellationToken: cancellationToken + ); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Providers\XHubContextFactory.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; +using xPushService.Interfaces; + +namespace xPushService.Providers +{ + public class XHubContextFactory : IXHubContextFactory + { + private readonly IServiceProvider services; + + public XHubContextFactory(IServiceProvider services) + { + this.services = services; + } + + public IHubContext Create() + where THub : Hub + { + return services.GetRequiredService>(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xPushService\Providers\XPushServiceUserNameProvider.cs + +using Microsoft.AspNetCore.SignalR; + +namespace xPushService.Providers { + public class XPushServiceUserNameProvider : IUserIdProvider { + public string GetUserId (HubConnectionContext connection) { + // + var result = connection.User?.Identity?.Name; + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Base\IXBaseService.cs + +namespace xServices.Base +{ + public interface IXBaseService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Base\XBaseService.cs + +namespace xServices.Base +{ + public class XBaseService : IXBaseService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Base\XTestProviderControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xServices.Interfaces; +using xServices.Models.Dtos; +using xServices.Models.Entities; +using xServices.Providers; + +namespace xServices.Base +{ + /// + /// an Abstract Implementation of Provided Actions for XTest ... + /// + public abstract class XTestProviderControllerBase : XBaseProvidedController, IXTestProviderController + { + protected XTestProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXTestProvider 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\xServices\Configurations\XServicesConfiguration.cs + +namespace xServices.Configurations +{ + public partial class XServicesConfiguration + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Configurations\XTestTypeConfiguration.cs + +using System; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xDataService.Providers; +using xServices.Models.Entities; + +namespace xServices.Configurations +{ + public class XTestTypeConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Constants\ConfigurationNodeNames.cs + +namespace xServices.Constants +{ + public partial struct ConfigurationNodeNames + { + public const string SERVICES_NODE = "ServicesConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Constants\xServicesConstancts.cs + +using xExceptions.Attributes; + +namespace xServices.Constants +{ + public class xServicesConstancts + { + } + + public enum XQueryServiceEndPoints + { + /// + /// Specified Endpoint for Users Fetching as Query ... + /// + [StringValue("Account/Users/Query")] + QueryUsers, + + /// + /// Refresh Tokens ... + /// + [StringValue("Account/RefreshTokens")] + RefreshTokens, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Controllers\XCategoryProviderController.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Controllers; +using xCategoryService.Interfaces; +using xCategoryService.Models.Entities; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; + +namespace xServices.Controllers +{ + public class XCategoryProviderController : XCategoryProviderControllerBase, IXCategoryProviderController + { + public XCategoryProviderController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXCategoryProvider 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\xServices\Controllers\XFileProviderController.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xFileService.Controllers; +using xFileService.Interfaces; +using xFileService.Models.Entities; +using xIdentityService.Interfaces; + +namespace xServices.Controllers +{ + public class XFileProviderController : XFileProviderControllerBase, IXFileProviderController + { + public XFileProviderController( + 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 + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Controllers\XStringProviderController.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xStringService.Controllers; +using xStringService.Interfaces; +using xStringService.Models.Entities; + +namespace xServices.Controllers +{ + public class XStringProviderController : XStringProviderControllerBase, IXStringProviderController + { + public XStringProviderController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXStringProvider 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\xServices\Controllers\XSubCategoryProviderController.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCategoryService.Controllers; +using xCategoryService.Interfaces; +using xCategoryService.Models.Entities; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; + +namespace xServices.Controllers +{ + public class XSubCategoryProviderController : XSubCategoryProviderControllerBase, IXSubCategoryProviderController + { + public XSubCategoryProviderController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXSubCategoryProvider 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\xServices\Controllers\XTagProviderController.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xTagService.Controllers; +using xTagService.Interfaces; +using xTagService.Models.Entities; + +namespace xServices.Controllers +{ + public class XTagProviderController : XTagProviderControllerBase, IXTagProviderController + { + public XTagProviderController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXTagProvider 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\xServices\Controllers\XTermsAndConditionsProviderController.cs + +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xTermsAndConditionsService.Controllers; +using xTermsAndConditionsService.Interfaces; +using xTermsAndConditionsService.Providers; + +namespace xServices.Controllers +{ + public class XTermsAndConditionsProviderController : XTermsAndConditionsControllerBase, IXTermsAndConditionsController + { + public XTermsAndConditionsProviderController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXTermsAndConditionsProvider provider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider, + provider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Controllers\XTestProviderController.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xServices.Base; +using xServices.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Controllers +{ + public class XTestProviderController : XTestProviderControllerBase, IXTestProviderController + { + public XTestProviderController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXTestProvider 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\xServices\Databases\XApiDatabaseDescriptor.cs + +using System; +using System.Collections.Generic; +using GraphQL.Types; +using xDataService.Interfaces; +using xDataService.Models; +using xServices.Interfaces; +using xServices.Models.Entities; +using xServices.Models.GraphQL; +using xServices.Providers; + +namespace xServices.Databases +{ + public class XApiDatabaseDescriptor : XDatabaseDescriptor, IXDatabaseDescriptor + { + public XApiDatabaseDescriptor() : base( + name: "xApiDb", + repositories: new List + { + // + // XTest Entity ... + new XEFRepositoryDescriptor< + XTest, + Guid, + IXTestKeyGenerator, + XTestKeyGenerator, + IXTestRepositoryEvents, + XTestRepositoryEvents, + XTestGraphType, + GuidGraphType, + XTestGraphQuery, + IXTestGraphQLTypeHelper, + XTestGraphQLTypeHelper, + XTestGraphSchema, + IXTestRepository, + XTestEFRepository, + XApiDbContext, + IXTestSeeder, + XTestSeeder + > + {} + } + ) + { + OptionsBuilder = b => b.MigrationsAssembly("xApi"); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Databases\XApiDbContext.cs + +using Microsoft.EntityFrameworkCore; +using xCategoryService.Providers; +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Providers; +using xFileService.Providers; +using xServices.Models.Entities; +using xStringService.Providers; +using xTagService.Providers; +using xWebinarService.Providers; +// using xStringService.Models.Entities; + +namespace xServices.Databases +{ + public class XApiDbContext : XDbContext + { + // + #region DbSets ... + public DbSet Tests { get; set; } + #endregion + + // + #region Navigation Property Entities ... + #endregion + + public XApiDbContext( + DbContextOptions options, + XDataServiceConfiguration config, + XEntityRegisterarHandlerService dynamicEntityHandlers + ) : base( + "xApiDb", + options, + config, + dynamicEntityHandlers, + new string[] { + nameof(XTagEntityRegisterar), + nameof(XFileEntityRegisterar), + nameof(XStringEntityRegisterar), + nameof(XWebinarEntityRegisterar), + nameof(XCategoryEntityRegisterar), + nameof(XSubCategoryEntityRegisterar), + nameof(XWebinarSubscriberEntityRegisterar), + } + ) + { } + + // + public override void OnXModelCreating(ModelBuilder modelBuilder) + { } + + // + public override void OnXConfiguring(DbContextOptionsBuilder optionsBuilder) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\DI\XDIHelperExtension.cs + +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCategoryService.DI; +using xCommons.Extensions; +using xDataService.DI; +using xDataService.Models; +using xFileService.DI; +using xIdentityService.DI; +using xMessageService.DI; +using xPushService.DI; +using xPushService.Helpers; +using xServices.Configurations; +using xServices.Constants; +using xServices.Databases; +using xServices.Interfaces; +using xServices.Providers; +using xStorageService.DI; +using xStringService.DI; +using xTagService.DI; +using xTermsAndConditionsService.DI; +using xWebinarService.DI; + +namespace xServices.DI +{ + public static partial class XDIHelperExtension + { + /// + /// Retrive Services Configurations ... + /// + /// + /// + public static XServicesConfiguration GetXServicesConfiguration( + this IConfiguration source + ) + { + // + var xConfig = source + .GetSection(ConfigurationNodeNames.SERVICES_NODE); + var result = xConfig.Get(); + + // + return result; + } + + /// + /// Register Service Configuration ... + /// + /// + /// + public static void AddXServicesConfiguration( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + var config = configuration + .GetXServicesConfiguration(); + if (config.IsNull()) + { + config = new XServicesConfiguration(); + } + + // + services.AddSingleton(config); + } + + /// + /// Register Service Configuration ... + /// + /// + /// + public static void AddXServicesConfiguration( + this IServiceCollection services, + XServicesConfiguration configuration + ) + { + // + if (configuration.IsNull()) + { + configuration = new XServicesConfiguration(); + } + + // + services.AddSingleton(configuration); + } + + /// + /// Register Services ... + /// + /// + /// + /// + /// + public static void AddXServices( + this IServiceCollection services, + IConfiguration configuration, + IEnumerable databases = null, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + var config = configuration.GetXServicesConfiguration(); + + // + AddServices( + config: config, + services: services, + lifeTime: lifeTime, + databases: databases, + configuration: configuration + ); + } + + /// + /// Register Services ... + /// + /// + /// + /// + /// + /// + public static void AddXServices( + this IServiceCollection services, + IConfiguration configuration, + XServicesConfiguration config, + IEnumerable databases = null, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + AddServices( + config: config, + services: services, + lifeTime: lifeTime, + databases: databases, + configuration: configuration + ); + } + + /// + /// Use All Required Middlewares ... + /// + /// + /// + /// + public static void UseXServices( + this IApplicationBuilder app, + IEnumerable databases = null, + bool isDevelopmentEnvironment = false + ) + { + // + // Use Storage Service ... + app.UseXStorageService(); + + // + #region Use Data Data Service ... + if (databases.HasChild()) + { + // + databases + .ToList() + .ForEach(database => + { + // + app.UseXDatabase( + descriptor: database, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + }); + } + #endregion + + // + #region Use Hubs ... + // + var pushHelper = new XPushServiceHelper(); + + // + pushHelper.AddHub("testDto"); + pushHelper.AddHub("testEntity"); + + // + app.UseXPushService(pushHelper); + #endregion + + // + // Using String Service ... + app.UseXStringService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + + // + // Using Terms and Conditions Service ... + app.UseXTermsAndConditionsService(); + + // + // Using Tag Service ... + app.UseXTagService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + + // + // Using File Service ... + app.UseXFileService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + + // + // Using Category Service ... + app.UseXCategoryService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + + // + // Using XWebinar Service ... + app.UsexWebinarService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + + // + #region Private ... + /// + /// Register Required Services ... + /// + /// + /// + /// + /// + /// + private static void AddServices( + this IServiceCollection services, + IConfiguration configuration, + XServicesConfiguration config = null, + IEnumerable databases = null, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + // Check config ... + if (config.IsNull()) + { + config = new XServicesConfiguration(); + } + + // + // Storage Service ... + services.AddXStorageService(configuration); + + // + // Message Service ... + services.AddXMessageService(config: configuration); + + // + #region Register Identity Service ... + services.AddXIdentityService( + configuration: configuration, + lifeTime: ServiceLifetime.Scoped + ); + #endregion + + // + #region Register Data Service ... + // + if (databases.HasChild()) + { + // + databases + .ToList() + .ForEach(database => + { + // + // Register DataBase ... + services.AddXDatabase( + lifeTime: lifeTime, + descriptor: database, + configuration: configuration + ); + }); + } + + // + // Register Repository Services ... + services.AddScoped(); + services.AddScoped(); + #endregion + + // + #region Register Push Service ... + // + // Push Service ... + services.AddXPushService( + config: configuration, + lifeTime: ServiceLifetime.Scoped + ); + + // + // Register Provider Services ... + // since Hubs required Identity Provider, you Have to Register IdentityService as Requirements ... + // a Provider Service, Provides Repository (Entity) or Service (Dto) based + // Data Manipulation Actions by Supporting Hub Contexts Events ... + services.AddScoped(); + services.AddScoped(); + #endregion + + // + var repositoryType = xDataService.Constants.XRepositoryType.EF; + + // + // Register String Service ... + services.AddXStringService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + + // + // Register Terms and Conditions Service ... + services.AddXTermsConditionsService( + lifeTime: lifeTime, + configuration: configuration + ); + + // + // Register Tag Service ... + services.AddXTagService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + + // + // Register File Service ... + services.AddXFileService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + + // + // Register Category Service ... + services.AddXCategoryServiceConfiguration(configuration); + services.AddXCategoryService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + + // + // Register Webinar Service ... + services.AddXWebinarService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Extensions\xServicesExtensions.cs + +namespace xServices.Extensions +{ + public static class xServicesExtensions + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestDtoHub.cs + +using System; +using xPushService.Interfaces; +using xServices.Models.Dtos; +using xServices.Models.Entities; + +namespace xServices.Interfaces +{ + public interface IXTestDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestEntityHub.cs + +using System; +using xPushService.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Interfaces +{ + public interface IXTestEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestGraphQLTypeHelper.cs + +using System; +using xDataService.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Interfaces +{ + public interface IXTestGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestGraphQuery.cs + +using System; +using GraphQL.Types; +using xDataService.Interfaces; +using xServices.Models.Entities; +using xServices.Models.GraphQL; + +namespace xServices.Interfaces +{ + public interface IXTestGraphQuery : IXBaseGraphQLQuery + {} +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestKeyGenerator.cs + +using System; +using xDataService.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Interfaces +{ + public interface IXTestKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestProvider.cs + +using System; +using xPushService.Interfaces; +using xServices.Models.Dtos; +using xServices.Models.Entities; +using xServices.Providers; + +namespace xServices.Interfaces +{ + public interface IXTestProvider : IXBaseProvided + {} +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestProviderController.cs + +using System; +using xPushService.Interfaces; +using xServices.Models.Dtos; +using xServices.Models.Entities; +using xServices.Providers; + +namespace xServices.Interfaces +{ + public interface IXTestProviderController : IXBaseProvidedController + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestRepository.cs + +using System; +using xDataService.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Interfaces +{ + public interface IXTestRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestRepositoryEvents.cs + +using xDataService.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Interfaces +{ + public interface IXTestRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestRepositoryProvider.cs + +using System; +using xPushService.Interfaces; +using xServices.Models.Entities; +using xServices.Providers; + +namespace xServices.Interfaces +{ + public interface IXTestRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestRepositoryService.cs + +using System; +using xDataService.Interfaces; +using xServices.Models.Dtos; +using xServices.Models.Entities; + +namespace xServices.Interfaces +{ + public interface IXTestRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestSeeder.cs + +using System; +using xDataService.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Interfaces +{ + public interface IXTestSeeder : IXBaseDbSeeder + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Interfaces\IXTestServiceProvider.cs + +using System; +using xPushService.Interfaces; +using xServices.Models.Dtos; +using xServices.Models.Entities; +using xServices.Providers; + +namespace xServices.Interfaces +{ + public interface IXTestServiceProvider : IXBaseDtoProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Models\Dtos\XTestDto.cs + +using System; +using xModels.Base; + +namespace xServices.Models.Dtos +{ + public class XTestDto : XBaseEntityDto + { + public string Firstname { get; set; } + public string Lastname { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Models\Entities\XTest.cs + +using System; +using xModels.Base; + +namespace xServices.Models.Entities +{ + public class XTest : XBaseEntity + { + public string Firstname { get; set; } + public string Lastname { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Models\GraphQL\XTestGraphType.cs + +using System; +using xDataService.GraphQL; +using xServices.Models.Entities; + +namespace xServices.Models.GraphQL +{ + public class XTestGraphType : XBaseGraphObjectType + { + public XTestGraphType() : base() + { + Field(x => x.Firstname); + Field(x => x.Lastname); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Models\XGroupedData.cs + +using System; +using System.Collections.Generic; +using xModels.Base; + +namespace xServices.Models +{ + public class XGroupedDataEntity + where T : XBaseEntity + { + public TGroupedKey GroupedBy { set; get; } + public IEnumerable Members { set; get; } + public DateTime TimeStamp { set; get; } + } + + public class XGroupedDataDto + where T : XBaseDto + { + public TGroupedKey GroupedBy { set; get; } + public IEnumerable Members { set; get; } + public DateTime TimeStamp { set; get; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestDtoHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xPushService.Base; +using xServices.Interfaces; +using xServices.Models.Dtos; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestDtoHub : XBaseDtoHub, IXTestDtoHub + { + public XTestDtoHub(ILogger logger) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestEFRepository.cs + +using System; +using xDataService.Configuration; +using xDataService.Providers; +using xDataService.Interfaces; +using xServices.Databases; +using xServices.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestEFRepository : XBaseEFRepository, IXTestRepository + { + public XTestEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXTestKeyGenerator keyGenerator = null, + IXTestRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestEntityHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xPushService.Base; +using xServices.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestEntityHub : XBaseEntityHub, IXTestEntityHub + { + public XTestEntityHub(ILogger logger) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestGraphQLTypeHelper.cs + +using System; +using xDataService.Configuration; +using xDataService.GraphQL; +using xServices.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXTestGraphQLTypeHelper + { + public XTestGraphQLTypeHelper( + XDataServiceConfiguration configuration + ) : base(configuration) + { + } + + public override string GetInQueryCollectionName() + { + return "tests"; + } + + public override string GetInQuerySingleName() + { + return "test"; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestGraphQuery.cs + +using System; +using GraphQL.Types; +using xDataService.Configuration; +using xDataService.GraphQL; +using xServices.Interfaces; +using xServices.Models.Entities; +using xServices.Models.GraphQL; + +namespace xServices.Providers +{ + public class XTestGraphQuery : XBaseGraphQLQuery + { + public XTestGraphQuery( + XDataServiceConfiguration configuration, + IXTestRepository repository, + IXTestGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xServices.Providers +{ + public class XTestGraphSchema : Schema + { + public XTestGraphSchema(IServiceProvider services) : base(services) + { + Query = (XTestGraphQuery)services.GetService(typeof(XTestGraphQuery)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestInMemoryRepository.cs + +using System; +using xDataService.Configuration; +using xDataService.Providers; +using xServices.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestInMemoryRepository : XBaseInMemoryRepository + { + public XTestInMemoryRepository( + XDataServiceConfiguration configuration, + IXTestKeyGenerator keyGenerator = null, + IXTestRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestKeyGenerator.cs + +using xDataService.Providers; +using xServices.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestKeyGenerator : XGuidKeyGenerator, IXTestKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestMongoRepository.cs + +using System; +using xDataService.Configuration; +using xDataService.Providers; +using xServices.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestMongoRepository : XBaseMongoRepository + { + public XTestMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXTestKeyGenerator keyGenerator = null, + IXTestRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestProvider.cs + +using System; +using xCommons.Extensions; +using xIdentityModels.Models; +using xPushService.Providers; +using xServices.Interfaces; +using xServices.Models.Dtos; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestProvider : XBaseProvided, IXTestProvider + { + public XTestProvider( + IXTestServiceProvider provider + ) : base( + provider, + permittedRoles: new string[] { } + ) + { } + + public override bool IsOwned( + XTestDto item, + XUserClaimsInfoDto userInfo + ) + { + return !item.IsNullOrDefault(); + } + + public override bool IsOwned( + XTest item, + XUserClaimsInfoDto userInfo + ) + { + return item.IsNullOrDefault(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestRepositoryEvents.cs + +using xDataService.Providers; +using xServices.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestRepositoryEvents : XBaseRepositoryEvents, IXTestRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestRepositoryProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; +using xServices.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestRepositoryProvider : XBaseEntityProvider, IXTestRepositoryProvider + { + public XTestRepositoryProvider( + IXTestRepository repository, + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub: hub, + repository: repository, + identityProvider: identityProvider, + dataConfiguration: dataConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestRepositoryService.cs + +using System; +using System.Collections.Generic; +using AutoMapper; +using xDataService.Providers; +using xServices.Interfaces; +using xServices.Models.Dtos; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestRepositoryService : XBaseRepositoryService, IXTestRepositoryService + { + public XTestRepositoryService( + IXTestRepository repository, + IEnumerable mapperProfiles = null + ) : base(repository, mapperProfiles) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestSeeder.cs + +using System; +using xDataService.Configuration; +using xDataService.Providers; +using xServices.Interfaces; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestSeeder : XBaseDbSeeder, IXTestSeeder + { + public XTestSeeder( + IXTestRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + database: "xApiDb", + entity: nameof(XTest), + repository: repository, + dataServiceConfiguration: dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xServices\Providers\XTestServiceProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; +using xServices.Interfaces; +using xServices.Models.Dtos; +using xServices.Models.Entities; + +namespace xServices.Providers +{ + public class XTestServiceProvider : XBaseDtoProvider, IXTestServiceProvider + { + public XTestServiceProvider( + IHubContext hub, + IXTestRepositoryService service, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub: hub, + service: service, + identityProvider: identityProvider, + dataConfiguration: dataConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\Constants\ConfigurationNodeNames.cs + +namespace xStorageService.Constants { + public partial struct ConfigurationNodeNames { + public const string STORAGE_SERVICE_NODE_NAME = "StorageConfiguration"; + } + +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\Constants\FileExtensions.cs + +using xExceptions.Attributes; + +namespace xStorageService.Constants { + public enum FileExtensions { + // + // Document + [StringValue (".txt")] + TEXT = 100, + + [StringValue (".pdf")] + PDF = 101, + + [StringValue (".ppt")] + PPT = 102, + + [StringValue (".pptx")] + PPTX = 103, + + [StringValue (".odp")] + ODP = 104, + + [StringValue (".pps")] + PPS = 105, + + [StringValue (".ods")] + ODS = 106, + + [StringValue (".xlr")] + XLR = 107, + + [StringValue (".xls")] + XLS = 108, + + [StringValue (".xlsx")] + XLSX = 109, + + [StringValue (".xml")] + XML = 110, + + [StringValue (".doc")] + DOC = 111, + + [StringValue (".docx")] + DOCX = 112, + + [StringValue (".odt")] + ODT = 113, + + [StringValue (".rtf")] + RTF = 114, + + [StringValue (".tex")] + TEX = 115, + + [StringValue (".wpd")] + WPD = 116, + + [StringValue (".md")] + MD = 117, + + // + // Archive + [StringValue (".zip")] + ZIP = 200, + + // + // Internal Supports + [StringValue (".ttf")] + TTF = 300, + + [StringValue (".js")] + JAVASCRIPT = 301, + + [StringValue (".css")] + CSS = 302, + + [StringValue (".htm")] + HTM = 303, + + [StringValue (".html")] + HTML = 304, + + [StringValue (".sdw")] + SAHER_DASHBOARD_WIDGET = 305, + + [StringValue (".sdwmanifest")] + SAHER_DASHBOARD_WIDGET_MANIFEST = 306, + + // + // Video + [StringValue (".mp4")] + MP4 = 400, + + [StringValue (".avi")] + AVI = 401, + + [StringValue (".mpg")] + MPG = 402, + + [StringValue (".mpeg")] + MPEG = 403, + + [StringValue (".wmv")] + WMV = 404, + + // + // Audio + [StringValue (".mp3")] + MP3 = 500, + + [StringValue (".ogg")] + OGG = 501, + + [StringValue (".wav")] + WAV = 502, + + [StringValue (".wma")] + WMA = 503, + + // + // Image + [StringValue (".png")] + PNG = 600, + + [StringValue (".jpeg")] + JPEG = 601, + + [StringValue (".jpg")] + JPG = 602, + + // [StringValue (".ico")] + // ICON = 603, + + [StringValue (".gif")] + GIF = 604, + + [StringValue (".bmp")] + BITMAP = 605, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\DI\XDIHelperExtension.cs + +using System.IO; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using xCommons.Extensions; +using xExceptions.Constants; +using xStorageService.Constants; +using xStorageService.Helpers; +using xStorageService.Interfaces; +using xStorageService.Models; +using xStorageService.Providers; + +namespace xStorageService.DI { + public static partial class XDIHelperExtension { + /// + /// Retrieve XStorage Configuration from AppSettings + /// + /// + /// + public static XStorageConfiguration GetXStorageConfiguration (this IConfiguration configuration) { + // + var storageConfigurationSection = configuration.GetSection (ConfigurationNodeNames.STORAGE_SERVICE_NODE_NAME); + return storageConfigurationSection.Get (); + } + + /// + /// Register XStorage Configuration + /// + /// + /// + public static void AddXStorageConfiguration ( + this IServiceCollection services, + IConfiguration configuration + ) { + // + // Check wwwroot Folder ... + if (!Directory.Exists ("wwwroot")) { + Directory.CreateDirectory ("wwwroot"); + } + + // + // Retrieve and Register XStorage Configurations ... + var xStorageConfiguration = configuration.GetXStorageConfiguration (); + if (xStorageConfiguration.IsNull ()) { + XException.InvalidConfiguration.Throw (); + } + + // + xStorageConfiguration.BaseFolder = Path.Combine (Directory.GetCurrentDirectory (), "wwwroot"); + xStorageConfiguration.RootFolder = Directory.GetDirectoryRoot (xStorageConfiguration.BaseFolder); + + // + services.AddSingleton (xStorageConfiguration); + } + + /// + /// Register XStorage Service + /// + /// + /// + public static void AddXStorageService ( + this IServiceCollection services, + IConfiguration configuration + ) { + // + // Register and Retrieve XStorage Configuration + services.AddXStorageConfiguration (configuration); + var xStorageConfiguration = services.GetRegisteredService (); + + // + // Add File Provider ... + services.AddSingleton ( + new PhysicalFileProvider (xStorageConfiguration.BaseFolder) + ); + + // + // Register XStorageHelper Service ... + services.AddSingleton (); + + // + // Register ThumbnailHelper ... + services.AddSingleton (); + + // + // Register Storage Provider ... + services.AddScoped (); + + // + // StorageService Preperation... + var xStorageService = services.GetRegisteredService (); + xStorageService.Prepare (); + } + + /// + /// Use XStorage Service + /// + /// + public static void UseXStorageService (this IApplicationBuilder app) { + // + // Get Instance of Scope and Service and Prepare Folder Structure ... + using (var scope = app.ApplicationServices.CreateScope ()) { + // + var storageProvider = scope.ServiceProvider.GetService (); + + // + storageProvider.Prepare (); + } + + // + // available Static Files ... + app.UseStaticFiles (); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\Helpers\XStorageHelper.cs + +using System; +using System.IO; +using System.Linq; +using xCommons.Constants; +using xCommons.Extensions; +using xExceptions.Constants; +using xStorageService.Constants; + +namespace xStorageService.Helpers { + public partial class XStorageHelper { + + private int[] DocumentTypeThreshold = new int[] { 99, 307 }; + private int[] ImageTypeThreshold = new int[] { 599, 606 }; + private int[] AudioTypeThreshold = new int[] { 499, 504 }; + private int[] VideoTypeThreshold = new int[] { 399, 405 }; + + public XStorageHelper () { } + + /// + /// Retrieve FileType + /// + /// + /// + public XFileType GetType (int fileExtension) { + // + // Compare File Extension Integer Value and + // Find Propper Type for it ... + if (fileExtension > DocumentTypeThreshold[0] && fileExtension < DocumentTypeThreshold[1]) { + return XFileType.Document; + } else if (fileExtension > VideoTypeThreshold[0] && fileExtension < VideoTypeThreshold[1]) { + return XFileType.Video; + } else if (fileExtension > AudioTypeThreshold[0] && fileExtension < AudioTypeThreshold[1]) { + return XFileType.Audio; + } else if (fileExtension > ImageTypeThreshold[0] && fileExtension < ImageTypeThreshold[1]) { + return XFileType.Image; + } else { + // + // if not in case return Document Type + return XFileType.Document; + } + } + + /// + /// Retrieve a File Type + /// + /// + /// + public XFileType GetType (string fileName) { + // + if (fileName.IsNullOrEmpty ()) { + XException.InavlidFile.Throw (); + } + + // + var ext = GetFileExtension (fileName); + var extType = GetExtensionType (ext); + return GetType (extType); + } + + /// + /// used for validating Extension in Widget Upload ... + /// + /// an string array + public string[] GetAllowedExtensionsForWidgetUpload () { + return new [] { + FileExtensions.SAHER_DASHBOARD_WIDGET.GetStringValue () + }; + } + + /// + /// Get a Files Extension string + /// + /// + /// + public string GetFileExtension (string fileName) { + // + // Validate Args ... + if (fileName.IsNullOrEmpty ()) { + return null; + } + + // + var fileExt = ""; + try { + fileExt = Path.GetExtension (fileName); + } catch { } + + // + return fileExt; + } + + /// + /// Get a File Name Without it's Extension + /// + /// + /// + public string GetFileNameWithoutExtension (string fileName) { + // + // Validate Args ... + if (fileName.IsNullOrEmpty ()) { + return null; + } + + // + var fileNameWithoutExt = ""; + try { + fileNameWithoutExt = Path + .GetFileNameWithoutExtension (fileName); + } catch { } + + // + return fileNameWithoutExt; + } + + /// + /// Get File Extension Type + /// + /// + /// + public int GetExtensionType (string fileExtension) { + // + foreach (var extItem in Enum + .GetValues (typeof (FileExtensions)) + .Cast ()) { + if (extItem.GetStringValue () + .Contains (fileExtension.ToLower ())) { + return (int) extItem; + } + } + + // + return -1; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\Interfaces\IXStorageProvider.cs + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.FileProviders; +using xCommons.Constants; +using xModels.Dtos; +using xStorageService.Constants; +using xStorageService.Helpers; +using xStorageService.Models; + +namespace xStorageService.Interfaces +{ + public partial interface IXStorageProvider + { + XStorageConfiguration Configuration { get; } + XStorageHelper StorageHelper { get; } + IFileProvider FileProvider { get; } + IXThumbnailProvider ThumbnailProvider { get; } + + // + #region Special Folders Path Getter (s) ... + string GetStorageFolderPath(); + string GetTempFolderPath(); + string GetThumbFolderPath(); + string GetUploadsFolderPath(); + string GetWidgetsFolderPath(); + string GetImageFolderPath(); + string GetAudioFolderPath(); + string GetVideoFolderPath(); + string GetDocumentFolderPath(); + string FilePathResolver(string fullPath); + string ConvertToFileUrl(string fullPath); + #endregion + + // + #region Prepare Storage Structure ... + void Prepare(); + #endregion + + // + #region Validators ... + bool IsFolderExists(string path); + bool IsFileExists(string filePath); + bool IsSupportThumb(string fileName); + bool IsSupportThumb(XFileType type); + void ValidateFileExists(string filePath); + void ValidateFileModel(XFileDto file); + void ValidateFile(IFormFile file); + void ValidateProfileImage(IFormFile file); + #endregion + + // + #region Generators ... + string NewFileName(); + string GenerateThumbName(string fileName); + string GetNewFileName(string fileName); + #endregion + + // + #region Tools Actions ... + + string GetFileExtension(string fileName); + string GetFileNameWithoutExtension(string fileName); + FileExtensions GetFileExtensionType(string fileName); + XFileType GetFileType(string fileName); + XFileType GetFileType(FileExtensions fileExtension); + string GetThumbnailFileFullPath(string thumFileName); + string GetRelatedThumbnailFullPath(string fileName); + string GetFileTypeFolderPath(XFileType type); + string GetFileFolderPath( + string fileName, + bool isTemp = false, + bool isWidget = false + ); + string GetFileFullPath( + string fileName, + bool isTemp = false, + bool isWidget = false + ); + Task SaveFileAsync( + IFormFile file, + bool isTemp = false, + bool isWidget = false, + CancellationToken cancellationToken = default + ); + void DeleteFile( + string fileFullPath, + bool forceFileExists = true, + Exception exception = null + ); + ICollection DeleteFiles( + ICollection fileFullPaths, + bool forceFileExists = true, + Exception exception = null + ); + // Task CreateThumbnail (XFile file); + Task CreateThumbnail( + XFileDto file, + CancellationToken cancellationToken = default + ); + Task CreateThumbnail( + string fileName, + CancellationToken cancellationToken = default + ); + Task HandleProfileImageSave( + IFormFile file, + CancellationToken cancellationToken = default + ); + Task HandleFileSave( + IFormFile file, + CancellationToken cancellationToken = default + ); + Task> HandleFilesSave( + IFormFileCollection files, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\Interfaces\IXThumbnailProvider.cs + +using xStorageService.Models; + +namespace xStorageService.Interfaces { + public partial interface IXThumbnailProvider { + void SetTempFolder (string folder); + XImageInfo GetImageInfo (string filePath); + void ResizeImage ( + string filePath, + string resizedFilePath, + int size, + int quality + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\Models\XFileResult.cs + +namespace xStorageService.Models { + public partial class XFileResult { + public string Name { get; set; } + public string FileName { get; set; } + public string FilePath { get; set; } + public string Thmbnail { get; set; } + public string ThmbnailPath { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\Models\XImageInfo.cs + +using ImageMagick; + +namespace xStorageService.Models { + public partial class XImageInfo { + public Interlace Interlace { get; } + public int Height { get; } + public MagickFormat Format { get; } + public string FileName { get; } + public Density Density { get; } + public CompressionMethod Compression { get; } + public ColorSpace ColorSpace { get; } + public int Width { get; } + public int Quality { get; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\Models\XStorageConfiguration.cs + +namespace xStorageService.Models { + public partial class XStorageConfiguration { + /// + /// Base Path of Identity Host Server + /// + /// + public string IdentityAuthority { get; set; } + + /// + /// Base Path of Host Server + /// + /// + public string Authority { get; set; } + + /// + /// Root Directory Of File Storage + /// + /// + public string RootFolder { get; set; } + + /// + /// Base Root of Usable Folder for Files + /// + /// + public string BaseFolder { get; set; } + + /// + /// Storage Folder Label + /// + /// + public string Storage { get; set; } + + /// + /// Temp Folder Label + /// + /// + public string Temp { get; set; } + + /// + /// Thumb Folder Label + /// + /// + public string Thumb { get; set; } + + /// + /// Uploads Folder Label + /// + /// + public string Uploads { get; set; } + + /// + /// Widgets Folder Label + /// + /// + public string Widgets { get; set; } + + /// + /// Images Folder Label + /// + /// + public string Image { get; set; } + + /// + /// Audios Folder Label + /// + /// + public string Audio { get; set; } + + /// + /// Videos Folder Label + /// + /// + public string Video { get; set; } + + /// + /// Documents Folder Label + /// + /// + public string Document { get; set; } + + /// + /// new Files Prefix + /// + /// + public string FilePrefix { get; set; } + + /// + /// Thumb Files Prefix + /// + /// + public string ThumbPrefix { get; set; } + + /// + /// Thumbnail File Sizes + /// + /// + public int ThumbSize { get; set; } + + /// + /// Thumbnail File Quality + /// + /// + public int ThumbQuality { get; set; } + + /// + /// Max File Size + /// + /// + public long MaxFileSize { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\Providers\XStorageProvider.cs + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.FileProviders; +using xCommons.Constants; +using xCommons.Extensions; +using xExceptions.Constants; +using xModels.Dtos; +using xStorageService.Constants; +using xStorageService.Helpers; +using xStorageService.Interfaces; +using xStorageService.Models; + +namespace xStorageService.Providers +{ + public partial class XStorageProvider : IXStorageProvider + { + + public readonly string BasePath; + private readonly XFileType[] ThumbSupprtedTypes = new XFileType[] { + XFileType.Image, XFileType.CoverImage, XFileType.ProfileImasge + }; + + public IFileProvider FileProvider { get; } + public XStorageHelper StorageHelper { get; } + public IXThumbnailProvider ThumbnailProvider { get; } + public XStorageConfiguration Configuration { get; } + + public XStorageProvider( + IFileProvider fileProvider, + XStorageHelper storageHelper, + IXThumbnailProvider thumbnailProvider, + XStorageConfiguration configuration + ) + { + // + FileProvider = fileProvider; + Configuration = configuration; + StorageHelper = storageHelper; + ThumbnailProvider = thumbnailProvider; + + // + BasePath = Path + .Combine(Directory + .GetCurrentDirectory(), Configuration.BaseFolder); + } + + // + #region Special Folders Path Getter (s) ... + /// + /// Get Storage Folder Path + /// + /// + public string GetStorageFolderPath() + { + return Path.Combine(BasePath, Configuration.Storage); + } + + /// + /// Get Temp Folder Path + /// + /// + public string GetTempFolderPath() + { + return Path.Combine(GetStorageFolderPath(), Configuration.Temp); + } + + /// + /// Get Uploads Folder Path + /// + /// + public string GetUploadsFolderPath() + { + return Path.Combine(GetStorageFolderPath(), Configuration.Uploads); + } + + /// + /// Get Widgets Folder Path + /// + /// + public string GetWidgetsFolderPath() + { + return Path.Combine(GetStorageFolderPath(), Configuration.Widgets); + } + + /// + /// Get Thumb Folder Path + /// + /// + public string GetThumbFolderPath() + { + return Path.Combine(GetUploadsFolderPath(), Configuration.Thumb); + } + + /// + /// Get Images Folder Path + /// + /// + public string GetImageFolderPath() + { + return Path.Combine(GetUploadsFolderPath(), Configuration.Image); + } + + /// + /// Get Audios Folder Path + /// + /// + public string GetAudioFolderPath() + { + return Path.Combine(GetUploadsFolderPath(), Configuration.Audio); + } + + /// + /// Get Videos Folder Path + /// + /// + public string GetVideoFolderPath() + { + return Path.Combine(GetUploadsFolderPath(), Configuration.Video); + } + + /// + /// Get Documents Folder Path + /// + /// + public string GetDocumentFolderPath() + { + return Path.Combine(GetUploadsFolderPath(), Configuration.Document); + } + + /// + /// Prepare Full Path to Use for Static Files + /// + /// + /// + public string FilePathResolver(string fullPath) + { + return fullPath.Replace(BasePath, ""); + } + + /// + /// Convert a File Path to Url + /// + /// + /// + public string ConvertToProfileImageUrl(string fullPath) + { + // + var authority = !Configuration.IdentityAuthority.IsNullOrEmpty() && + Configuration.IdentityAuthority.EndsWith("/") ? + Configuration.IdentityAuthority.Substring(0, Configuration.IdentityAuthority.Length - 1) : + Configuration.IdentityAuthority; + + // + return $"{authority}{FilePathResolver(fullPath)}"; + } + + /// + /// Convert a File Path to Url + /// + /// + /// + public string ConvertToFileUrl(string fullPath) + { + // + var authority = !Configuration.Authority.IsNullOrEmpty() && + Configuration.Authority.EndsWith("/") ? + Configuration.Authority.Substring(0, Configuration.Authority.Length - 1) : + Configuration.Authority; + + // + return $"{authority}/{FilePathResolver(fullPath)}"; + } + #endregion + + // + #region Prepare Storage Structure ... + /// + /// Check Folder Structure of Storage Area + /// and Create them if not Exists + /// + public void Prepare() + { + // + try + { + // + // Storage Folder Path Check ... + if (!IsFolderExists(GetStorageFolderPath())) + { + Directory.CreateDirectory(GetStorageFolderPath()); + } + + // + // Temp Folder Path Check ... + if (!IsFolderExists(GetTempFolderPath())) + { + Directory.CreateDirectory(GetTempFolderPath()); + } + + // + // Temp Folder Path Check ... + if (!IsFolderExists(GetThumbFolderPath())) + { + Directory.CreateDirectory(GetThumbFolderPath()); + } + + // + // Widgets Folder Path Chekc ... + if (!IsFolderExists(GetWidgetsFolderPath())) + { + Directory.CreateDirectory(GetWidgetsFolderPath()); + } + + // + // Uploads Folder Path Chekc ... + if (!IsFolderExists(GetUploadsFolderPath())) + { + Directory.CreateDirectory(GetUploadsFolderPath()); + } + + // + // Image Folder Path Chekc ... + if (!IsFolderExists(GetImageFolderPath())) + { + Directory.CreateDirectory(GetImageFolderPath()); + } + + // + // Music Folder Path Chekc ... + if (!IsFolderExists(GetAudioFolderPath())) + { + Directory.CreateDirectory(GetAudioFolderPath()); + } + + // + // Video Folder Path Chekc ... + if (!IsFolderExists(GetVideoFolderPath())) + { + Directory.CreateDirectory(GetVideoFolderPath()); + } + + // + // Document Folder Path Chekc ... + if (!IsFolderExists(GetDocumentFolderPath())) + { + Directory.CreateDirectory(GetDocumentFolderPath()); + } + } + catch + { + XException.StorageServiceInitialFailed.Throw(); + } + + // + // Set Temp Folder for Actions ... + ThumbnailProvider.SetTempFolder(GetTempFolderPath()); + } + #endregion + + // + #region Validators ... + /// + /// Detrmines a Path Exists or not + /// + /// + /// + public bool IsFolderExists(string path) + { + return Directory.Exists(path); + } + + /// + /// Check a File Exists or not + /// + /// + /// + public bool IsFileExists(string filePath) + { + return File.Exists(filePath); + } + + /// + /// Check a File Name Support Thumbnail or not + /// + /// + /// + public bool IsSupportThumb(string fileName) + { + // + if (!fileName.StartsWith(Configuration.FilePrefix)) + { + XException.InavlidFile.Throw(); + } + + // + var fileType = StorageHelper.GetType(fileName); + return IsSupportThumb(fileType); + } + + /// + /// Check a FileType Support Thumbnail or not + /// + /// + /// + public bool IsSupportThumb(XFileType type) + { + return ThumbSupprtedTypes.Contains(type); + } + + /// + /// Validate a File Exists or not + /// + /// + public void ValidateFileExists(string filePath) + { + // + if (!IsFileExists(filePath)) + { + XException.NotFound.Throw(); + } + } + + /// + /// Validate a File Model with it's Related Physical File + /// + /// + public void ValidateFileModel(XFileDto file) + { + // + // Validate Args ... + if (file == null) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Full File Path ... + var fileFullPath = file.Path; + if (fileFullPath.IsNullOrEmpty()) + { + XException.InavlidFile.Throw(); + } + + // + // Retrieve is File Exists or not ... + var isPhysicalFileExists = IsFileExists(file.Path); + if (!isPhysicalFileExists) + { + XException.NotFound.Throw(); + } + } + + /// + /// Validate File for Upload + /// + /// + public void ValidateFile(IFormFile file) + { + // + // Validate Args ... + if (file == null) + { + XException.InvalidArgs.Throw(); + } + + // + // Check File Size not Zero ... + if (file.Length == 0) + { + XException.EmptyFile.Throw(); + } + + // + // Check File Max Size ... + if (file.Length > Configuration.MaxFileSize) + { + XException.MaxFileSizeExceeded.Throw(); + } + } + + /// + /// Validate a Collection of IFormFile + /// + /// + public void ValidateFiles(IFormFileCollection files) + { + // + // Validate Args ... + if (!files.HasChild()) + { + XException.InvalidArgs.Throw(); + } + + // + files.ToList() + .ForEach(f => ValidateFile(f)); + } + + /// + /// Validate File for Profile Image + /// + /// + public void ValidateProfileImage(IFormFile file) + { + // + // Check File Validation ... + // since Args Checked in File Validation, there is no need to + // double check it ... + ValidateFile(file); + + // + // Check File Type ... + var fileType = GetFileType(file.FileName); + if (fileType != XFileType.Image) + { + XException.UnsupportedFileType.Throw(); + } + } + #endregion + + // + #region Generators ... + /// + /// Generate New File + /// + /// + public string NewFileName() + { + return Configuration.FilePrefix + Guid.NewGuid().ToString().ToLower(); + } + + /// + /// Retrieve Related Thumnail File + /// + /// + /// + public string GenerateThumbName(string fileName) + { + // + if (!IsSupportThumb(fileName)) + { + XException.UnsupportedFileType.Throw(); + } + + // + return fileName.Replace(Configuration.FilePrefix, Configuration.ThumbPrefix); + } + + /// + /// Get a New FileName with Extension based on Given fileName + /// + /// + /// + public string GetNewFileName(string fileName) + { + // + // Validate Args ... + if (fileName.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + var fileExt = GetFileExtension(fileName); + var newFileNameWithoutExt = NewFileName(); + + // + // Generate New File Name ... + var newFileName = string.Format("{0}{1}", newFileNameWithoutExt, fileExt); + + // + return newFileName; + } + #endregion + + // + #region Tools Actions ... + /// + /// Extract a FileName's Extension + /// + /// + /// + public string GetFileExtension(string fileName) + { + // + var result = StorageHelper.GetFileExtension(fileName); + if (result.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + return result; + } + + /// + /// Extract a FileName's Without it's Extension + /// + /// + /// + public string GetFileNameWithoutExtension(string fileName) + { + // + var result = StorageHelper.GetFileNameWithoutExtension(fileName); + if (result.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + return result; + } + + /// + /// Get file Extension of Specified File Name + /// + /// + /// + public FileExtensions GetFileExtensionType(string fileName) + { + // + var fileExt = GetFileExtension(fileName); + if (fileExt.IsNullOrEmpty()) + { + XException.InvalidData.Throw(); + } + + // + var result = StorageHelper.GetExtensionType(fileExt); + if (result == -1) + { + XException.UnsupportedFileType.Throw(); + } + + // + return (FileExtensions)result; + } + + /// + /// Return File Type of Specific File Name + /// + /// + /// + public XFileType GetFileType(string fileName) + { + // + var fileExtension = GetFileExtensionType(fileName); + return GetFileType(fileExtension); + } + + /// + /// Return File Type of Specific Extension + /// + /// + /// + public XFileType GetFileType(FileExtensions fileExtension) + { + return StorageHelper.GetType((int)fileExtension); + } + + /// + /// Retrieve Full Path of thumbnail File + /// + /// + /// + public string GetThumbnailFileFullPath(string thumFileName) + { + // + if (!thumFileName.StartsWith(Configuration.ThumbPrefix)) + { + XException.InavlidFile.Throw(); + } + + // + return Path.Combine(GetThumbFolderPath(), thumFileName); + } + + /// + /// Retrieve a File Thumbnail Path if Supported + /// + /// + /// + public string GetRelatedThumbnailFullPath(string fileName) + { + // + var thumbName = GenerateThumbName(fileName); + return GetThumbnailFileFullPath(thumbName); + } + + /// + /// Retrieve Special File Type's Folder Path + /// + /// + /// + public string GetFileTypeFolderPath(XFileType type) + { + // + // Retrieve Folder Path Based on file ... + switch (type) + { + case XFileType.Image: + case XFileType.CoverImage: + case XFileType.ProfileImasge: + return GetImageFolderPath(); + + case XFileType.Audio: + return GetAudioFolderPath(); + + case XFileType.Video: + return GetVideoFolderPath(); + + case XFileType.Document: + default: + return GetDocumentFolderPath(); + } + } + + /// + /// Retrieve a File's Folder path + /// + /// + /// + /// + /// + public string GetFileFolderPath( + string fileName, + bool isTemp = false, + bool isWidget = false + ) + { + // + // Validate Args ... + if (fileName.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + var fileType = GetFileType(fileName); + + // + var folderPath = ""; + + // + // if isTemp equals true + // ignore file type and return Temp Folder ... + if (isTemp) + { + folderPath = GetTempFolderPath(); + } + + // + // if isWidget equals true and isTemp equals false + // ignore file type and return Widget's Folder ... + if (isWidget && !isTemp) + { + folderPath = GetWidgetsFolderPath(); + } + + // + // if both isTemp and isWidget equals false + // which is the default state, retrieve proper folder path + // based on file type and return it ... + if (!isTemp && !isWidget) + { + folderPath = GetFileTypeFolderPath(fileType); + } + + // + // Check folderPath restrict to have Value ... + if (folderPath.IsNullOrEmpty()) + { + XException.InvalidData.Throw(); + } + + // + // return result ... + return folderPath; + } + + /// + /// Retrieve a Full path of a File + /// + /// + /// + /// + /// + public string GetFileFullPath( + string fileName, + bool isTemp = false, + bool isWidget = false + ) + { + // + var folderPath = GetFileFolderPath( + fileName, + isTemp: isTemp, + isWidget: isWidget + ); + + // + if (folderPath.IsNullOrEmpty()) + { + XException.ActionFailed.Throw(); + } + + // + return Path.Combine(folderPath, fileName); + } + + /// + /// Save a File into Storage Based on it's Type + /// + /// + /// + /// + /// + /// + public async Task SaveFileAsync( + IFormFile file, + bool isTemp = false, + bool isWidget = false, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + ValidateFile(file); + + // + // Extract and Generate required Data ... + var fileName = GetNewFileName(file.FileName); + var folderPath = GetFileFolderPath(file.FileName, isTemp, isWidget); + var fileFullPath = Path.Combine(folderPath, fileName); + + // + // Save File ... + using (var stream = new FileStream(fileFullPath, FileMode.Create)) + { + // + await file.CopyToAsync( + stream, + cancellationToken + ); + } + + // + // Since all Required data can be Generated based on + // a simple file Name, there is no need to return additional + // data as result ... + return fileName; + } + + /// + /// Delete a Physical File by Validate it's Exists + /// + /// + /// + /// + public void DeleteFile( + string fileFullPath, + bool forceFileExists = true, + Exception exception = null + ) + { + // + // Validate Args ... + if (!forceFileExists && + fileFullPath.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.NotFound.ToException(); + } + + // + // Check File Exists ... + var isFileExists = File.Exists(fileFullPath); + if (forceFileExists && + !isFileExists) + { + XException.NotFound.Throw(); + } + + // + // Handle Prevent Error when forceFileExists false + // and File doesn't Exists ... + if (!forceFileExists && + !isFileExists) + { + return; + } + + // + File.Delete(fileFullPath); + } + + /// + /// Remove a List of Physical Files Path + /// and return Un Removable Files ... + /// + /// + /// + /// + /// + public ICollection DeleteFiles( + ICollection fileFullPaths, + bool forceFileExists = true, + Exception exception = null + ) + { + // + if (exception.IsNull()) + { + exception = XException.NotFound.ToException(); + } + + // + var result = fileFullPaths.Where(f => !IsFileExists(GetFileFullPath(f))).ToList(); + if (forceFileExists && result.HasChild()) + { + throw exception; + } + + // + var existsFile = fileFullPaths.Where(f => IsFileExists(f)).ToList(); + existsFile.ForEach(f => + { + DeleteFile( + f, + forceFileExists: forceFileExists, + exception: exception + ); + }); + + // + return fileFullPaths.Except(existsFile).ToList(); + } + + /// + /// Create Thumbnail based on FileName.Ext + /// + /// + /// + /// + public async Task CreateThumbnail( + string fileName, + CancellationToken cancellationToken = default + ) + { + // + if (fileName.IsNullOrEmpty()) + { + return; + } + + // + await Task.Run(() => + { + // + var fileFullPath = GetFileFullPath(fileName); + if (!IsFileExists(fileFullPath)) + { + XException.NotFound.Throw(); + } + + // + var thumbFileName = GenerateThumbName(fileName); + var thumbFullPath = GetThumbnailFileFullPath(thumbFileName); + + // + ThumbnailProvider.ResizeImage( + fileFullPath, + thumbFullPath, + Configuration.ThumbSize, + Configuration.ThumbQuality + ); + }, cancellationToken); + } + + /// + /// Create Thumbnail based on XFile model + /// + /// + /// + /// + public async Task CreateThumbnail( + XFileDto file, + CancellationToken cancellationToken = default + ) + { + // + await CreateThumbnail( + file.Name, + cancellationToken + ); + } + + /// + /// Save and Create a Thumbnail For Profile Image + /// + /// + /// + /// + public async Task HandleProfileImageSave( + IFormFile file, + CancellationToken cancellationToken = default + ) + { + // + ValidateProfileImage(file); + + // + var savedFileName = await SaveFileAsync( + file, + isTemp: false, + isWidget: false, + cancellationToken: cancellationToken + ); + var thumbFileName = GenerateThumbName(savedFileName); + + // + var savedFileFullPath = GetFileFullPath(savedFileName); + var thumbFullPath = GetThumbnailFileFullPath(thumbFileName); + + // + await CreateThumbnail( + savedFileName, + cancellationToken + ); + + // + return new XFileResult + { + Name = file.Name, + FileName = savedFileName, + FilePath = ConvertToProfileImageUrl(savedFileFullPath), + Thmbnail = thumbFileName, + ThmbnailPath = ConvertToProfileImageUrl(thumbFullPath) + }; + } + + /// + /// Handle File Upload with Support of Thumbnails ... + /// + /// + /// + /// + public async Task HandleFileSave( + IFormFile file, + CancellationToken cancellationToken = default + ) + { + // + ValidateFile(file); + + // + var savedFileName = await SaveFileAsync(file, isTemp: false, isWidget: false); + + // + var savedFileFullPath = GetFileFullPath(savedFileName); + + // + var thumbFileName = ""; + var thumbFullPath = ""; + var fileType = GetFileType(savedFileName); + if (fileType.IsImageKind()) + { + // + thumbFileName = GenerateThumbName(savedFileName); + thumbFullPath = GetThumbnailFileFullPath(thumbFileName); + + // + await CreateThumbnail(savedFileName); + } + + // + var result = + new XFileResult + { + Name = file.FileName, + FileName = savedFileName, + FilePath = ConvertToFileUrl(savedFileFullPath), + Thmbnail = thumbFileName, + ThmbnailPath = ConvertToFileUrl(thumbFullPath), + }; + + // + return result; + } + + /// + /// Handle File Upload with Support of Thumbnails ... + /// + /// + /// + /// + public async Task> HandleFilesSave( + IFormFileCollection files, + CancellationToken cancellationToken = default + ) + { + // + ValidateFiles(files); + + // + var result = new Collection(); + foreach (var file in files) + { + // + var savedFileName = await SaveFileAsync( + file, + isTemp: false, + isWidget: false, + cancellationToken: cancellationToken + ); + + // + var savedFileFullPath = GetFileFullPath(savedFileName); + + // + var thumbFileName = ""; + var thumbFullPath = ""; + var fileType = GetFileType(savedFileName); + if (fileType.IsImageKind()) + { + // + thumbFileName = GenerateThumbName(savedFileName); + thumbFullPath = GetThumbnailFileFullPath(thumbFileName); + + // + await CreateThumbnail( + savedFileName, + cancellationToken + ); + } + + // + result.Add( + new XFileResult + { + Name = file.FileName, + FileName = savedFileName, + FilePath = ConvertToFileUrl(savedFileFullPath), + Thmbnail = thumbFileName, + ThmbnailPath = ConvertToFileUrl(thumbFullPath), + }); + } + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStorageService\Providers\XThumbnailProvider.cs + +using ImageMagick; +using xCommons.Extensions; +using xStorageService.Interfaces; +using xStorageService.Models; + +namespace xStorageService.Providers { + public partial class XThumbnailProvider : IXThumbnailProvider { + public void SetTempFolder (string folder) { + MagickNET.SetTempDirectory (folder); + } + + public XImageInfo GetImageInfo (string filePath) { + // + MagickImageInfo info = new MagickImageInfo (filePath); + return info.MapConvert (); + } + + public void ResizeImage ( + string filePath, + string resizedFilePath, + int size, + int quality + ) { + // + using (var image = new MagickImage (filePath)) { + // + image.Resize (size, size); + image.Strip (); + image.Quality = quality; + image.Write (resizedFilePath); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Configurations\Entities\XStringEntityConfiguration.cs + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xDataService.Providers; +using xStringService.Constants; +using xStringService.Models.Entities; + +namespace xStringService.Configurations.Entities +{ + public class XStringEntityConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable(XStringServiceConstants.XStringTable); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Constants\XStringServiceConstants.cs + +namespace xStringService.Constants +{ + public struct XStringServiceConstants + { + // + // Service Extensions Log Tag ... + public const string XStringServiceDILogTag = "XStringService"; + + // + // Table Mapping Identifiers ... + public const string XStringTable = "Strings"; + + // + // GraphQL Collection Mappings ... + public const string XStringSingleName = "string"; + public const string XStringCollectionName = "strings"; + + // + // Hubs ... + public const string XStringDtoHub = "stringDto"; + public const string XStringEntityHub = "stringEntity"; + + // + // Custome Constants ... + + // + // EF Repositories Helper Extensions ... + public const string GetXStringEFRepositoryDescriptor = "GetXStringEFRepositoryDescriptor"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Controllers\XBaseItemResourceProviderControllerBase.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; +using xModels.Dtos; +using xStringService.Interfaces; +using xStringService.Models.Dtos; + +namespace xStringService.Controllers +{ + public abstract class XBaseItemResourceProviderControllerBase : XBaseIdentityApiV1Controller, IXBaseItemResourceProviderController + { + private readonly IXItemResourceProvider provider; + + protected XBaseItemResourceProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXItemResourceProvider provider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { + this.provider = provider; + } + + // + #region Actions ... + /// + /// Retrieve a List of Exists Languages ... + /// + /// if null or empty, check all languages in Item Side + /// + /// + [HttpGet("Languages")] + public virtual async Task>> GetLanguages( + [FromQuery] object identifier, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = Enumerable.Empty(); + if (!identifier.IsNull()) + { + // + result = await provider.GetLanguages( + identifier: identifier, + cancellationToken: cancellationToken + ); + } + else + { + // + result = await provider.GetLanguages( + cancellationToken: cancellationToken + ); + } + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve all Exists Items ... + /// + /// Specified Resource Title + /// + /// + [HttpGet("ResourceLocales")] + public virtual async Task>> GetResourceLocales( + [FromQuery] object identifier, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = Enumerable.Empty(); + if (!identifier.IsNull()) + { + // + result = await provider.GetResourceLocales( + identifier: identifier, + cancellationToken: cancellationToken + ); + } + else + { + // + result = await provider.GetResourceLocales( + cancellationToken: cancellationToken + ); + } + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get all Exists Items for Specified identitifer ... + /// + /// + /// + /// + [HttpGet("Locales/{identifier}")] + public virtual async Task>> GetLocales( + [FromRoute] object identifier, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + + // + return Ok(); + } + catch (Exception ex) + { + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Specified Language Locale of Specified Identifier ... + /// + /// + /// if null or empty Remove all Locales + /// + /// + [HttpDelete("Locales/{identifier}")] + public virtual async Task> RemoveLocale( + [FromRoute] object identifier, + [FromQuery] string language, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + var isValid = !identifier.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Connection ID ... + var connectionId = GetConnectionId(); + + // + XResourceDto result = null; + if (!language.IsNullOrEmpty()) + { + // + result = await provider.RemoveLocale( + language: language, + identifier: identifier, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + else + { + // + await provider.RemoveLocales( + identifier: identifier, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Check Specified ocale Exists for Specified Identifier ... + /// + /// + /// + /// + /// + [HttpGet("Locales/{identifier}/{language}/Has")] + public virtual async Task> HasLocale( + [FromRoute] object identifier, + [FromRoute] string language, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(language) + .AddNotNull(identifier) + .ValidateGroupAsync(); + + // + // Calculate Result ... + var result = await provider.HasLocale( + language: language, + identifier: identifier, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get all Exists Locales Representaion based on Specified Identitifer + /// as XResourceDto ... + /// + /// + /// + /// + [HttpGet("Resource/{identifier}")] + public virtual async Task> GetResource( + [FromRoute] object identifier, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + ValidationProvider.NotNull(identifier); + + // + var result = await provider.GetResource( + identifier: identifier, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add Or Update Locale for Specified Identitifer ... + /// + /// + /// + /// + /// + [HttpPost("Locales/{identifier}")] + public virtual async Task> AddOrUpdateLocale( + [FromRoute] object identifier, + [FromBody] XLocaleResourceDto locale, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(identifier) + .AddNotNull(locale) + .AddNotEmpty(locale.Value) + .AddNotEmpty(locale.Language) + .ValidateGroupAsync(); + + // + // Retrieve Connection ID ... + var connectionId = GetConnectionId(); + + // + var result = await provider.AddOrUpdateLocale( + locale: locale, + identifier: identifier, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update a Resource State ... + /// + /// + /// + /// + /// + [HttpPost("Resource/{identifier}")] + public virtual async Task> AddOrUpdateResource( + [FromRoute] object identifier, + [FromBody] XResourceDto resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(identifier) + .AddNotNull(resource) + .ValidateGroupAsync(); + + // + // Retrieve Connection ID ... + var connectionId = GetConnectionId(); + + // + var result = await provider.AddOrUpdateResource( + resource: resource, + identifier: identifier, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Controllers\XBaseResourceProviderControllerBase.cs + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; +using xModels.Dtos; +using xStringService.Interfaces; +using xStringService.Models.Dtos; + +namespace xStringService.Controllers +{ + public abstract class XBaseResourceProviderControllerBase : XBaseIdentityApiV1Controller, IXBaseResourceProviderController + where TResourceProvider : IXResourceProvider + { + private readonly TResourceProvider provider; + + protected XBaseResourceProviderControllerBase( + ILogger> logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + TResourceProvider provider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { + this.provider = provider; + } + + #region Actions ... + /// + /// Retrieve a List of Exists Languages ... + /// + /// + /// + [HttpGet("Languages")] + public virtual async Task>> GetLanguages( + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetLanguages(cancellationToken); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve all Exists Items ... + /// + /// Specified Resource Title + /// + /// + [HttpGet("LocalesResource")] + public virtual async Task>> GetResourceLocales( + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetResourceLocales(cancellationToken); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Extract Translation of in Specified Language ... + /// + /// Specified Language ... + /// + /// + [HttpGet("{language}/Translation")] + public virtual async Task> GetResourceTranslation( + [FromRoute] string language, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetResourceTranslation( + language: language, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Check Has Specified Translation or not ... + /// + /// Specified Language ... + /// + /// + [HttpGet("{language}/Has")] + public virtual async Task> HasLocale( + [FromRoute] string language, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .HasLocale( + language: language, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve as Locale Model ... + /// + /// Specified Language ... + /// + /// + [HttpGet("{language}/Locale")] + public virtual async Task> GetLocale( + [FromRoute] string language, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetLocale( + language: language, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve all Exists Locale Models ... + /// + /// + /// + [HttpGet("Locales")] + public virtual async Task>> GetLocales( + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetLocales(cancellationToken); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve as XResourceDto ... + /// + /// + /// + [HttpGet("Resource")] + public virtual async Task> GetResource( + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetResource(cancellationToken); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add or Update a Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + [HttpPost("")] + public virtual async Task> AddOrUpdateResource( + [FromBody] XResourceDto resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .AddOrUpdateResource( + resource: resource, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add Or Update Locale ... + /// + /// + /// + /// + [HttpPost("Locales")] + public virtual async Task> AddOrUpdateLocale( + [FromBody] XLocaleResourceDto locale, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .AddOrUpdateLocale( + locale: locale, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Locale ... + /// + /// + /// + /// + [HttpPost("Locale/Remove")] + public virtual async Task> RemoveLocale( + [FromBody] XLocaleResourceDto locale, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .RemoveLocale( + locale: locale, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Specified Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + [HttpPost("Resource/Remove")] + public virtual async Task RemoveResource( + [FromBody] XResourceDto resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + await provider + .RemoveResource( + resource: resource, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Controllers\XStringProviderControllerBase.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityService.Interfaces; +using xModels.Dtos; +using xPushService.Controllers; +using xStringService.Interfaces; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; +using xStringService.Providers.Dtos; + +namespace xStringService.Controllers +{ + public abstract class XStringProviderControllerBase : XBaseProvidedController, IXStringProviderController + { + private readonly IXStringProvider provider; + + protected XStringProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXStringProvider provider, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider, + provider, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { + this.provider = provider; + } + + // + #region Tools ... + /// + /// Retrieve a List of Exists Languages ... + /// + /// + /// + [HttpGet("Languages")] + public virtual async Task>> GetLanguages( + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetLanguages(cancellationToken); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve a List of Resource Titles ... + /// + /// if provided, returns all language Specified resource titles, if not, all Resource Titles + /// + /// + [HttpGet("ResourceTitles")] + public virtual async Task>> GetResourceTitles( + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetResourceTitles( + language: language, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve all Exists Items which belongs to Specified Resource Title ... + /// + /// Specified Resource Title + /// + /// + [HttpGet("ResourceLocales")] + public virtual async Task>> GetResourceLocales( + [FromQuery] string resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetResourceLocales( + resource: resource, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Extract Translation of a Resource in Specified Language ... + /// + /// Specified Language ... + /// Specified Resource Title ... + /// + /// + [HttpGet("Translation")] + public virtual async Task> GetResourceTranslation( + [FromQuery] string language, + [FromQuery] string resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetResourceTranslation( + language: language, + resource: resource, + cancellationToken: cancellationToken + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve a Resource as Locale Model ... + /// + /// Specified Language ... + /// Specified Resource Title ... + /// + /// + [HttpGet("Locale")] + public virtual async Task> GetLocale( + [FromQuery] string language, + [FromQuery] string resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetLocale( + language: language, + resource: resource, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve all Exists Resource Instances as Locale Models ... + /// + /// + /// + /// + [HttpGet("Locales")] + public virtual async Task>> GetLocales( + [FromQuery] string resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetLocales( + resource: resource, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve all Locales of Specified Resource and Represent as XResourceDto ... + /// + /// + /// + /// + [HttpGet("GetResource")] + public virtual async Task> GetResource( + [FromQuery] string resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + var result = await provider + .GetResource( + resource: resource, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add or Update a Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + [HttpPost("AddOrUpdateResource")] + public virtual async Task> AddOrUpdateResource( + [FromBody] XResourceDto resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(resource) + .AddNotEmpty(resource.Resource) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider + .AddOrUpdateResource( + resource: resource, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Specified Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + [HttpPost("RemoveResource")] + public virtual async Task RemoveResource( + [FromBody] XResourceDto resource, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(resource) + .AddNotEmpty(resource.Resource) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + await provider + .RemoveResource( + resource: resource, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Controllers\XStringRepositoryControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xDataService.Controllers; +using xDataService.Interfaces; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Controllers +{ + public abstract class XStringRepositoryControllerBase : XBaseRepositoryController, IXBaseRepositoryController + { + protected XStringRepositoryControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXStringRepository repository, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repository, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Controllers\XStringRepositoryProviderControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xPushService.Interfaces; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; +using xStringService.Providers.Entities; + +namespace xStringService.Controllers +{ + public abstract class XStringRepositoryProviderControllerBase : XBaseRepositoryProviderController, IXBaseRepositoryProviderController + { + protected XStringRepositoryProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXStringRepositoryProvider 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\xStringService\Controllers\XStringServiceControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xDataService.Controllers; +using xDataService.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; + +namespace xStringService.Controllers +{ + public abstract class XStringServiceControllerBase : XBaseServiceController, IXBaseServiceController + { + protected XStringServiceControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXStringRepositoryService repositoryService, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repositoryService, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Controllers\XStringServiceProviderControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xPushService.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; +using xStringService.Providers.Dtos; + +namespace xStringService.Controllers +{ + public abstract class XStringServiceProviderControllerBase : XBaseServiceProviderController, IXBaseServiceProviderController + { + protected XStringServiceProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXStringServiceProvider 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\xStringService\DI\XDIHelperExtension.cs + +using System; +using Microsoft.Extensions.DependencyInjection; +using xExceptions.Constants; +using xCommons.Extensions; +using xDataService.Constants; +using xDataService.Db; +using xDataService.DI; +using xDataService.Models; +using xStringService.Helpers; +using xCommons.Helpers; +using Microsoft.AspNetCore.Builder; +using xStringService.Interfaces.Dtos; +using xStringService.Providers.Dtos; +using xStringService.Interfaces.Entities; +using xStringService.Providers.Entities; +using xPushService.Helpers; +using xPushService.DI; +using xStringService.Interfaces; +using xStringService.Providers; +using xStringService.Constants; + +namespace xStringService.DI +{ + public static class XDIHelperExtension + { + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXStringService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + AddStringService( + contextType: null, + services: services, + lifeTime: lifeTime, + repositoryType: repositoryType + ); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + public static void AddXStringService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + where TContext : XDbContext + { + AddStringService( + services: services, + lifeTime: lifeTime, + contextType: typeof(TContext), + repositoryType: repositoryType + ); + } + + /// + /// Use XStringService Middleware ... + /// + /// + /// + public static void UseXStringService( + this IApplicationBuilder app, + bool isDevelopmentEnvironment = false + ) + { + // + #region Using XString Hubs ... + // + var pushHelper = new XPushServiceHelper(); + + // + pushHelper.AddHub(XStringServiceConstants.XStringDtoHub); + pushHelper.AddHub(XStringServiceConstants.XStringEntityHub); + + // + app.UseXPushService(pushHelper); + #endregion + + // + // Using XString 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 = XStringServiceConstants.XStringServiceDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + private static void AddStringService( + 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(XStringServiceHelper) + .InvokeGenericMethod( + args: null, + runtimeType: contextType, + methodName: XStringServiceConstants.GetXStringEFRepositoryDescriptor + ); + break; + + // + case XRepositoryType.Mongo: + descriptor = XStringServiceHelper.GetXStringMongoRepositoryDescriptor(); + break; + + // + case XRepositoryType.InMemory: + descriptor = XStringServiceHelper.GetXStringInMemoryRepositoryDescriptor(); + break; + } + + // + // Register XString Repository Descriptor ... + services.AddXRepository( + lifeTime: lifeTime, + descriptor: descriptor + ); + + // + // Register Dto Services ... + services.Add( + new ServiceDescriptor(typeof(IXStringServiceProvider), typeof(XStringServiceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXStringRepositoryService), typeof(XStringRepositoryService), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXStringRepositoryProvider), typeof(XStringRepositoryProvider), lifeTime) + ); + + // + // Register XString Provider ... + services.Add( + new ServiceDescriptor(typeof(IXStringProvider), typeof(XStringProvider), lifeTime) + ); + + // + Log("Service Regitration Succeed ..."); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Extensions\XModelExtensions.cs + +using System.Collections.Generic; +using System.Linq; +using xCommons.Extensions; +using xModels.Dtos; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; + +namespace xStringService.Extensions +{ + public static class XModelExtensions + { + /// + /// Converts an Entity to LocaleResource Dto ... + /// + /// + /// + public static XLocaleResourceDto ToXLocaleResourceDto(this XString source) + { + // + var result = new XLocaleResourceDto + { + Language = source.IsNull() ? "" : source.Language, + Value = source.IsNull() ? "" : source.TranslatedValue, + }; + + // + return result; + } + + /// + /// Converts an Dto to LocaleResource Dto ... + /// + /// + /// + public static XLocaleResourceDto ToXLocaleResourceDto(this XStringDto source) + { + // + var result = new XLocaleResourceDto + { + Language = source.IsNull() ? "" : source.Language, + Value = source.IsNull() ? "" : source.TranslatedValue, + }; + + // + return result; + } + + /// + /// Converts a Collection of Entities to Locale Resource Presentation ... + /// + /// + /// + public static IEnumerable ToXLocaleResourceDtos(this IEnumerable source) + { + // + var result = source.Select(e => ToXLocaleResourceDto(e)); + return result; + } + + /// + /// Converts a Collection of Dtos to Locale Resource Presentation ... + /// + /// + /// + public static IEnumerable ToXLocaleResourceDtos(this IEnumerable source) + { + // + var result = source.Select(e => ToXLocaleResourceDto(e)); + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Helpers\XStringServiceHelper.cs + +using GraphQL.Types; +using xDataHelper.Providers.GraphQL; +using xDataService.Db; +using xDataService.Models; +using xDataService.Providers; +using xStringService.Interfaces; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; +using xStringService.Providers.Entities; +using xStringService.Providers.GraphQL; + +namespace xStringService.Helpers +{ + public static class XStringServiceHelper + { + // + #region EF Repository Descriptor Provider(s) ... + public static XEFRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringEFRepository, + TContext + > GetXStringEFRepositoryDescriptor() + where TContext : XDbContext + { + // + var result = new XEFRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringEFRepository, + TContext + >(); + + // + return result; + } + + public static XEFRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringEFRepository, + TContext, + IXStringSeeder, + TSeederImplementation + > GetXStringEFRepositoryDescriptor() + where TContext : XDbContext + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XEFRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringEFRepository, + TContext, + IXStringSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region Mongo Repository Descriptor Provider(s) ... + public static XMongoRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringMongoRepository + > GetXStringMongoRepositoryDescriptor() + { + // + var result = new XMongoRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringMongoRepository + >(); + + // + return result; + } + + public static XMongoRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringMongoRepository, + IXStringSeeder, + TSeederImplementation + > GetXStringMongoRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XMongoRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringMongoRepository, + IXStringSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region InMemory Repository Descriptor Provider(s) ... + public static XInMemoryRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringInMemoryRepository + > GetXStringInMemoryRepositoryDescriptor() + { + // + var result = new XInMemoryRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringInMemoryRepository + >(); + + // + return result; + } + + public static XInMemoryRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringInMemoryRepository, + IXStringSeeder, + TSeederImplementation + > GetXStringInMemoryRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XInMemoryRepositoryDescriptor< + XString, + int, + IXStringKeyGenerator, + XStringKeyGenerator, + IXStringRepositoryEvents, + XStringRepositoryEvents, + XStringGraphType, + IntGraphType, + XStringGraphQuery, + IXStringGraphQLTypeHelper, + XStringGraphQLTypeHelper, + XStringGraphSchema, + IXStringRepository, + XStringInMemoryRepository, + IXStringSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\Dtos\IXStringDtoHub.cs + +using xPushService.Interfaces; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; + +namespace xStringService.Interfaces.Dtos +{ + public interface IXStringDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\Dtos\IXStringRepositoryService.cs + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using xDataService.Interfaces; +using xModels.Dtos; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; + +namespace xStringService.Interfaces.Dtos +{ + public interface IXStringRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\Dtos\IXStringServiceProvider.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xCommons.Configurations; +using xModels.Dtos; +using xPushService.Interfaces; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; +using xStringService.Providers.Dtos; + +namespace xStringService.Interfaces.Dtos +{ + public interface IXStringServiceProvider : IXBaseDtoProvider + { + /// + /// Application Configuration ... + /// + XAppConfiguration AppConfiguration { get; } + + // + #region Retrievers ... + /// + /// Retrieve all Exists Languages ... + /// + /// + /// + Task> GetLanguages( + CancellationToken cancellationToken = default + ); + + /// + /// Check Specified Resource Exists or not ... + /// + /// + /// + /// + /// + Task IsResourceExists( + string resource, + string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Specified Resource ... + /// + /// + /// + /// + /// + Task Get( + string resource, + string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Specified Translation of Specified Resource ... + /// + /// + /// + /// + /// + Task GetResourceValue( + string resource, + string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve an Specified Resource Items ... + /// + /// + /// + /// + Task> GetResources( + string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Specified Resource Items as XResourceDto Presentation ... + /// + /// + /// + /// + Task GetResource( + string resource, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Query ... + /// + /// Query Resource IDs ... + /// + /// + /// + /// + Task> QueryResourceIds( + XQuery query, + CancellationToken cancellationToken = default + ); + + /// + /// Query Specified Language Resources ... + /// + /// + /// + /// + /// + Task> QueryLanguageResources( + XQuery query, + string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Query Locale Resources ... + /// + /// + /// + /// + /// + Task> QueryLocaleResources( + XQuery query, + string language = null, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Add/Update Actions ... + /// + /// Add Specified Resource ... + /// + /// + /// + /// + /// + /// + /// + Task Add( + string resource, + string value, + string language = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update Resource ... + /// + /// + /// + /// + /// + /// + /// + Task Update( + string resource, + string value, + string language = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Or Update Resource ... + /// + /// + /// + /// + /// + /// + /// + Task AddOrUpdate( + string resource, + string value, + string language = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Specified Locale Resource ... + /// + /// + /// + /// + /// + /// + Task AddByLocaleResource( + XLocaleResourceDto item, + string resource, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update Specified Locale Resource ... + /// + /// + /// + /// + /// + /// + Task UpdateByLocaleResource( + XLocaleResourceDto item, + string resource, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Or Update Resource By Locale ... + /// + /// + /// + /// + /// + /// + Task AddOrUpdateByLocaleResource( + XLocaleResourceDto item, + string resource, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Or Update all XResourceDto(s) Locales ... + /// + /// + /// + /// + /// + Task> AddByResource( + XResourceDto item, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Remove Actions ... + /// + /// Remove all Specified Language's Resources ... + /// + /// + /// + /// + /// + Task RemoveLanguageResources( + string language, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove all Specified Resource Ids instances ... + /// + /// + /// + /// + /// + Task> RemoveResource( + string resource, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Specified Resource ... + /// + /// + /// + /// + /// + /// + Task Remove( + string resource, + string language = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove all Resource of Specified XResourceDto ... + /// + /// + /// + /// + /// + Task RemoveByResource( + XResourceDto item, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\Entities\IXStringEntityHub.cs + +using xPushService.Interfaces; +using xStringService.Models.Entities; + +namespace xStringService.Interfaces.Entities +{ + public interface IXStringEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\Entities\IXStringGraphQLTypeHelper.cs + +using xDataService.Interfaces; +using xStringService.Models.Entities; + +namespace xStringService.Interfaces.Entities +{ + public interface IXStringGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\Entities\IXStringKeyGenerator.cs + +using xDataService.Interfaces; +using xStringService.Models.Entities; + +namespace xStringService.Interfaces.Entities +{ + public interface IXStringKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\Entities\IXStringRepository.cs + +using xDataService.Interfaces; +using xStringService.Models.Entities; + +namespace xStringService.Interfaces.Entities +{ + public interface IXStringRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\Entities\IXStringRepositoryEvents.cs + +using xDataService.Interfaces; +using xStringService.Models.Entities; + +namespace xStringService.Interfaces.Entities +{ + public interface IXStringRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\Entities\IXStringRepositoryProvider.cs + +using xPushService.Interfaces; +using xStringService.Models.Entities; +using xStringService.Providers.Entities; + +namespace xStringService.Interfaces.Entities +{ + public interface IXStringRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\Entities\IXStringSeeder.cs + +using xDataService.Interfaces; +using xStringService.Models.Entities; + +namespace xStringService.Interfaces.Entities +{ + public interface IXStringSeeder : IXBaseDbSeeder + {} +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\IXBaseItemResourceProviderController.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using xModels.Dtos; +using xStringService.Models.Dtos; + +namespace xStringService.Interfaces +{ + public interface IXBaseItemResourceProviderController + { + /// + /// Retrieve a List of Exists Languages ... + /// + /// if null or empty, check all languages in Item Side + /// + /// + [HttpGet("Languages")] + Task>> GetLanguages( + [FromQuery] object identifier, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists Items ... + /// + /// Specified Resource Title + /// + /// + [HttpGet("ResourceLocales")] + Task>> GetResourceLocales( + [FromQuery] object identifier, + CancellationToken cancellationToken = default + ); + + /// + /// Get all Exists Items for Specified identitifer ... + /// + /// + /// + /// + [HttpGet("Locales/{identifier}")] + Task>> GetLocales( + [FromRoute] object identifier, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Specified Language Locale of Specified Identifier ... + /// + /// + /// if null or empty Remove all Locales + /// + /// + [HttpDelete("Locales/{identifier}")] + Task> RemoveLocale( + [FromRoute] object identifier, + [FromQuery] string language, + CancellationToken cancellationToken = default + ); + + /// + /// Check Specified ocale Exists for Specified Identifier ... + /// + /// + /// + /// + /// + [HttpGet("Locales/{identifier}/{language}/Has")] + Task> HasLocale( + [FromRoute] object identifier, + [FromRoute] string language, + CancellationToken cancellationToken = default + ); + + /// + /// Get all Exists Locales Representaion based on Specified Identitifer + /// as XResourceDto ... + /// + /// + /// + /// + [HttpGet("Resource/{identifier}")] + Task> GetResource( + [FromRoute] object identifier, + CancellationToken cancellationToken = default + ); + + /// + /// Add Or Update Locale for Specified Identitifer ... + /// + /// + /// + /// + /// + [HttpPost("Locales/{identifier}")] + Task> AddOrUpdateLocale( + [FromRoute] object identifier, + [FromBody] XLocaleResourceDto locale, + CancellationToken cancellationToken = default + ); + + /// + /// Update a Resource State ... + /// + /// + /// + /// + /// + [HttpPost("Resource/{identifier}")] + Task> AddOrUpdateResource( + [FromRoute] object identifier, + [FromBody] XResourceDto resource, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\IXBaseResourceProviderController.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using xModels.Dtos; +using xStringService.Models.Dtos; + +namespace xStringService.Interfaces +{ + public interface IXBaseResourceProviderController + { + /// + /// Retrieve a List of Exists Languages ... + /// + /// + /// + [HttpGet("Languages")] + Task>> GetLanguages( + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists Items ... + /// + /// Specified Resource Title + /// + /// + [HttpGet("LocalesResource")] + Task>> GetResourceLocales( + CancellationToken cancellationToken = default + ); + + /// + /// Extract Translation of in Specified Language ... + /// + /// Specified Language ... + /// + /// + [HttpGet("{language}/Translation")] + Task> GetResourceTranslation( + [FromRoute] string language, + CancellationToken cancellationToken = default + ); + + /// + /// Check Has Specified Translation or not ... + /// + /// Specified Language ... + /// + /// + [HttpGet("{language}/Has")] + Task> HasLocale( + [FromRoute] string language, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve as Locale Model ... + /// + /// Specified Language ... + /// + /// + [HttpGet("{language}/Locale")] + Task> GetLocale( + [FromRoute] string language, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists Locale Models ... + /// + /// + /// + [HttpGet("Locales")] + Task>> GetLocales( + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve as XResourceDto ... + /// + /// + /// + [HttpGet("Resource")] + Task> GetResource( + CancellationToken cancellationToken = default + ); + + /// + /// Add or Update a Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + [HttpPost("")] + Task> AddOrUpdateResource( + [FromBody] XResourceDto resource, + CancellationToken cancellationToken = default + ); + + /// + /// Add Or Update Locale ... + /// + /// + /// + /// + [HttpPost("Locales")] + Task> AddOrUpdateLocale( + [FromBody] XLocaleResourceDto locale, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Locale ... + /// + /// + /// + /// + [HttpPost("Locale/Remove")] + Task> RemoveLocale( + [FromBody] XLocaleResourceDto locale, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Specified Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + [HttpPost("Resource/Remove")] + Task RemoveResource( + [FromBody] XResourceDto resource, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\IXItemResourceProvider.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xModels.Dtos; +using xStringService.Models.Dtos; + +namespace xStringService.Interfaces +{ + public interface IXItemResourceProvider + { + // + #region Group Actions ... + /// + /// Retrieve a List of Exists Languages for Current Item ... + /// + /// + /// + Task> GetLanguages( + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists Items of type ... + /// + /// + /// + Task> GetResourceLocales( + CancellationToken cancellationToken = default + ); + + /// + /// Remove all Exists Locales which Related to this Provider ... + /// + /// + /// + /// + Task RemoveResourceLocales( + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Item Actions ... + /// + /// Get all Exists Items for Specified identitifer ... + /// + /// + /// + /// + Task> GetResourceLocales( + object identifier, + CancellationToken cancellationToken = default + ); + + /// + /// Get all Exists Items for Specified identitifer ... + /// + /// + /// + /// + Task> GetLocales( + object identifier, + CancellationToken cancellationToken = default + ); + + /// + /// Get all Exists Locales Representaion based on Specified Identitifer + /// as XResourceDto ... + /// + /// + /// + /// + Task GetResource( + object identifier, + CancellationToken cancellationToken = default + ); + + /// + /// Add Or Update Locale for Specified Identitifer ... + /// + /// + /// + /// + /// + /// + Task AddOrUpdateLocale( + object identifier, + XLocaleResourceDto locale, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update a Resource State ... + /// + /// + /// + /// + /// + /// + Task AddOrUpdateResource( + object identifier, + XResourceDto resource, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Specified Language Locale of Specified Identifier ... + /// + /// + /// + /// + /// + /// + Task RemoveLocale( + object identifier, + string language, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove all Exists Locales for Specified Identifier ... + /// + /// + /// + /// + /// + Task RemoveLocales( + object identifier, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Check Specified ocale Exists for Specified Identifier ... + /// + /// + /// + /// + /// + Task HasLocale( + object identifier, + string language, + CancellationToken cancellationToken = default + ); + + /// + /// Get all Exists Languages for Specified Identifier ... + /// + /// + /// + /// + Task> GetLanguages( + object identifier, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Resource Title Action ... + /// + /// Retrieve Resource Title ... + /// + /// + /// + string GetResourceTitle(object identifier = null); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\IXResourceProvider.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xModels.Dtos; +using xStringService.Models.Dtos; + +namespace xStringService.Interfaces +{ + public interface IXResourceProvider + { + /// + /// Retrieve a List of Exists Languages ... + /// + /// + /// + Task> GetLanguages( + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists Items ... + /// + /// + /// + Task> GetResourceLocales( + CancellationToken cancellationToken = default + ); + + /// + /// Extract Translation of in Specified Language ... + /// + /// Specified Language ... + /// + /// + Task GetResourceTranslation( + string language, + CancellationToken cancellationToken = default + ); + + /// + /// Check Has Specified Translation or not ... + /// + /// Specified Language ... + /// + /// + Task HasLocale( + string language, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve as Locale Model ... + /// + /// Specified Language ... + /// + /// + Task GetLocale( + string language, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists Locale Models ... + /// + /// + /// + Task> GetLocales( + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve as XResourceDto ... + /// + /// + /// + Task GetResource( + CancellationToken cancellationToken = default + ); + + /// + /// Add or Update a Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + /// + Task AddOrUpdateResource( + XResourceDto resource, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Or Update Locale ... + /// + /// + /// + /// + /// + Task AddOrUpdateLocale( + XLocaleResourceDto locale, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Locale ... + /// + /// + /// + /// + /// + Task RemoveLocale( + XLocaleResourceDto locale, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Specified Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + /// + Task RemoveResource( + XResourceDto resource, + string connectionId = null, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\IXStringProvider.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xIdentityModels.Models; +using xModels.Dtos; +using xPushService.Interfaces; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; +using xStringService.Providers.Dtos; + +namespace xStringService.Interfaces +{ + public interface IXStringProvider : IXBaseProvided + { + // + #region Tools ... + /// + /// Retrieve a List of Exists Languages ... + /// + /// + /// + Task> GetLanguages( + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve a List of Resource Titles ... + /// + /// if provided, returns all language Specified resource titles, if not, all Resource Titles + /// + /// + Task> GetResourceTitles( + string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists Items which belongs to Specified Resource Title ... + /// + /// Specified Resource Title + /// + /// + Task> GetResourceLocales( + string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Extract Translation of a Resource in Specified Language ... + /// + /// Specified Language ... + /// Specified Resource Title ... + /// + /// + Task GetResourceTranslation( + string language, + string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve a Resource as Locale Model ... + /// + /// Specified Language ... + /// Specified Resource Title ... + /// + /// + Task GetLocale( + string language, + string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists Resource Instances as Locale Models ... + /// + /// + /// + /// + Task> GetLocales( + string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Locales of Specified Resource and Represent as XResourceDto ... + /// + /// + /// + /// + Task GetResource( + string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Add or Update a Resource Model ... + /// + /// a XResourceDto Model ... + /// Actor User for Permissions ... + /// + /// + /// + Task AddOrUpdateResource( + XResourceDto resource, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Specified Resource Model ... + /// + /// a XResourceDto Model ... + /// Actor User for Permissions ... + /// + /// + /// + Task RemoveResource( + XResourceDto resource, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Interfaces\IXStringProviderController.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using xModels.Dtos; +using xPushService.Interfaces; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; +using xStringService.Providers.Dtos; + +namespace xStringService.Interfaces +{ + public interface IXStringProviderController : IXBaseProvidedController + { + // + #region Tools ... + /// + /// Retrieve a List of Exists Languages ... + /// + /// + /// + [HttpGet("Languages")] + Task>> GetLanguages( + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve a List of Resource Titles ... + /// + /// if provided, returns all language Specified resource titles, if not, all Resource Titles + /// + /// + [HttpGet("ResourceTitles")] + Task>> GetResourceTitles( + [FromQuery] string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists Items which belongs to Specified Resource Title ... + /// + /// Specified Resource Title + /// + /// + [HttpGet("ResourceLocales")] + Task>> GetResourceLocales( + [FromQuery] string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Extract Translation of a Resource in Specified Language ... + /// + /// Specified Language ... + /// Specified Resource Title ... + /// + /// + [HttpGet("Translation")] + Task> GetResourceTranslation( + [FromQuery] string language, + [FromQuery] string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve a Resource as Locale Model ... + /// + /// Specified Language ... + /// Specified Resource Title ... + /// + /// + [HttpGet("Locale")] + Task> GetLocale( + [FromQuery] string language, + [FromQuery] string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Exists Resource Instances as Locale Models ... + /// + /// + /// + /// + [HttpGet("Locales")] + Task>> GetLocales( + [FromQuery] string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve all Locales of Specified Resource and Represent as XResourceDto ... + /// + /// + /// + /// + [HttpGet("GetResource")] + Task> GetResource( + [FromQuery] string resource, + CancellationToken cancellationToken = default + ); + + /// + /// Add or Update a Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + [HttpPost("AddOrUpdateResource")] + Task> AddOrUpdateResource( + [FromBody] XResourceDto resource, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Specified Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + [HttpPost("RemoveResource")] + Task RemoveResource( + [FromBody] XResourceDto resource, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Models\Dtos\XStringDto.cs + +using xModels.Base; + +namespace xStringService.Models.Dtos +{ + public class XStringDto : XBaseIntIDEntityDto + { + public string Language { get; set; } + public string ResourceTitle { get; set; } + public string TranslatedValue { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Models\Entities\XString.cs + +using System.ComponentModel.DataAnnotations; +using xModels.Base; + +namespace xStringService.Models.Entities +{ + public class XString : XBaseIntIDEntity + { + [Required] + [StringLength (10)] + public string Language { get; set; } + + [Required] + [StringLength (255)] + public string ResourceTitle { get; set; } + + [Required] + public string TranslatedValue { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Dtos\XStringDtoHub.cs + +using Microsoft.Extensions.Logging; +using xPushService.Base; +using xStringService.Interfaces.Dtos; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Dtos +{ + public class XStringDtoHub : XBaseDtoHub, IXStringDtoHub + { + public XStringDtoHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Dtos\XStringRepositoryService.cs + +using System.Collections.Generic; +using AutoMapper; +using xDataService.Providers; +using xStringService.Interfaces.Dtos; +using xStringService.Interfaces.Entities; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Dtos +{ + public class XStringRepositoryService : XBaseRepositoryService, IXStringRepositoryService + { + public XStringRepositoryService( + IXStringRepository repository, + IEnumerable mapperProfiles = null + ) : base( + repository, + mapperProfiles + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Dtos\XStringServiceProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using xCommons.Configurations; +using xCommons.Extensions; +using xDataService.Configuration; +using xExceptions.Constants; +using xIdentityService.Interfaces; +using xModels.Dtos; +using xPushService.Base; +using xPushService.Constants; +using xStringService.Extensions; +using xStringService.Interfaces.Dtos; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Dtos +{ + public class XStringServiceProvider : XBaseDtoProvider, IXStringServiceProvider + { + public XAppConfiguration AppConfiguration { get; } + + public XStringServiceProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXStringRepositoryService service, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + dataConfiguration, + service, + identityProvider + ) + { + AppConfiguration = appConfiguration; + } + + // + #region Retrievers ... + /// + /// Retrieve all Exists Languages ... + /// + /// + /// + public async Task> GetLanguages( + CancellationToken cancellationToken = default + ) + { + // + var result = (await Service.GetAllAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + orderBuilder: x => x.OrderBy(y => y.Language) + )) + .AsQueryable() + .Select(e => e.Language) + .Distinct() + .ToList(); + + // + // Check Configuration Default Language Exists ... + var hasDefaultLanguage = !AppConfiguration.DefaultLanguage.IsNullOrEmpty(); + if (hasDefaultLanguage) + { + // + // Find Default Language on Result ... + hasDefaultLanguage = result.Any(e => e.ToNormalString() == AppConfiguration.DefaultLanguage.ToNormalString()); + if (!hasDefaultLanguage) + { + result.Add(AppConfiguration.DefaultLanguage); + } + } + + // + result = result + .Distinct() + .ToList(); + + // + return result; + } + + /// + /// Check Specified Resource Exists or not ... + /// + /// + /// + /// + /// + public async Task IsResourceExists( + string resource, + string language = null, + CancellationToken cancellationToken = default + ) + { + // + var result = false; + + // + // Validate ... + result = !resource.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + XStringDto dto = null; + if (language.IsNullOrEmpty()) + { + // + dto = await Service + .FindOneAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: e => e.ResourceTitle == resource + ); + } + else + { + // + dto = await Service + .FindOneAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: + e => e.ResourceTitle == resource && + e.Language.ToNormalString() == language.ToNormalString() + ); + } + + // + result = !dto.IsNullOrDefault(); + + // + return result; + } + + /// + /// Retrieve Specified Resource ... + /// + /// + /// + /// + /// + public async Task Get( + string resource, + string language = null, + CancellationToken cancellationToken = default + ) + { + // + // Normalize ... + if (language.IsNullOrEmpty()) + { + language = AppConfiguration.DefaultLanguage; + } + + // + // Validate ... + if (resource.IsNullOrEmpty() || language.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Resource Exists ... + var isExists = await IsResourceExists( + resource: resource, + language: language + ); + if (!isExists) + { + XException.NotFound.Throw(); + } + + // + var result = await Service + .FindOneAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: e => + e.ResourceTitle == resource && + e.Language.ToNormalString() == language.ToNormalString() + ); + if (result.IsNullOrDefault()) + { + XException.ActionFailed.Throw(); + } + + // + return result; + } + + /// + /// Retrieve Specified Translation of Specified Resource ... + /// + /// + /// + /// + /// + public async Task GetResourceValue( + string resource, + string language = null, + CancellationToken cancellationToken = default + ) + { + // + var result = ""; + + // + // Normalize ... + if (language.IsNullOrEmpty()) + { + language = AppConfiguration.DefaultLanguage; + } + + // + // Validate ... + var isValid = + !resource.IsNullOrEmpty() && + !language.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Langauage Exists ... + var languages = await GetLanguages(cancellationToken); + isValid = languages.Any(l => l.ToNormalString() == language.ToNormalString()); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Check Resource Exists ... + isValid = await IsResourceExists( + resource: resource, + language: language, + cancellationToken: cancellationToken + ); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + var dto = await Service.FindOneAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: e => + e.ResourceTitle == resource && + e.Language.ToNormalString() == language.ToNormalString() + ); + isValid = !dto.IsNullOrDefault(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + + // + result = dto.TranslatedValue; + + // + return result; + } + + /// + /// Retrieve an Specified Resource Items ... + /// + /// + /// + /// + public async Task> GetResources( + string resource, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = await IsResourceExists(resource); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + var result = await Service + .FindManyAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + predicate: e => + e.ResourceTitle == resource, + orderBuilder: x => + x.OrderBy(y => y.ResourceTitle), + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Retrieve Specified Resource Items as XResourceDto Presentation ... + /// + /// + /// + /// + public async Task GetResource( + string resource, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + bool isExists = await IsResourceExists( + language: null, + resource: resource, + cancellationToken: cancellationToken + ); + if (!isExists) + { + XException.NotFound.Throw(); + } + + // + // Retrieve String Dto (s) ... + var resources = await GetResources( + resource: resource, + cancellationToken: cancellationToken + ); + if (!resource.HasChild()) + { + XException.NotFound.Throw(); + } + + // + var result = new XResourceDto + { + Resource = resource, + Locales = resources.ToXLocaleResourceDtos() + }; + + // + return result; + } + #endregion + + // + #region Query ... + /// + /// Query Resource IDs ... + /// + /// + /// + /// + public async Task> QueryResourceIds( + XQuery query, + CancellationToken cancellationToken = default + ) + { + // + var dtoResult = await Service.QueryAsync( + query: query, + predicate: null, + includeBuilder: null, + ignoreSoftDeleteds: true, + orderBuilder: x => + x.OrderBy(y => y.ResourceTitle), + cancellationToken: cancellationToken + ); + + // + var result = new XQueryResult() + .UpdateData( + updateWith: dtoResult, + propertyBlackList: new List + { + nameof(XQueryResult.Items) + }); + + // + result.Items = dtoResult.Items.ToList().Select(dto => dto.ResourceTitle); + + // + return result; + } + + /// + /// Query Specified Language Resources ... + /// + /// + /// + /// + /// + public async Task> QueryLanguageResources( + XQuery query, + string language = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await Service.QueryAsync( + query: query, + includeBuilder: null, + ignoreSoftDeleteds: true, + orderBuilder: x => x + .OrderBy(y => y.Language) + .ThenBy(y => y.ResourceTitle), + cancellationToken: cancellationToken, + predicate: x => + x.Language.ToNormalString() == language.ToNormalString() + ); + + // + return result; + } + + /// + /// Query Locale Resources ... + /// + /// + /// + /// + /// + public async Task> QueryLocaleResources( + XQuery query, + string language = null, + CancellationToken cancellationToken = default + ) + { + // + Expression> predicate = null; + if (!language.IsNullOrEmpty()) + { + // + predicate = x => + x.Language.ToNormalString() == language.ToNormalString(); + } + + // + var dtoResult = await Service.QueryAsync( + query: query, + includeBuilder: null, + predicate: predicate, + ignoreSoftDeleteds: true, + orderBuilder: x => x + .OrderBy(y => y.Language) + .ThenBy(y => y.ResourceTitle), + cancellationToken: cancellationToken + ); + + // + var result = new XQueryResult() + .UpdateData( + updateWith: dtoResult, + propertyBlackList: new List + { + nameof(XQueryResult.Items) + }); + + // + result.Items = dtoResult.Items.ToList().ToXLocaleResourceDtos(); + + // + return result; + } + #endregion + + // + #region Add/Update Actions ... + /// + /// Add Specified Resource ... + /// + /// + /// + /// + /// + /// + /// + public async Task Add( + string resource, + string value, + string language = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + bool result = false; + + // + // Normalize ... + if (language.IsNullOrEmpty()) + { + language = AppConfiguration.DefaultLanguage; + } + + // + // Validate ... + result = + !value.IsNullOrEmpty() && + !resource.IsNullOrEmpty() && + !language.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + // Check Exists ... + bool isExists = await IsResourceExists( + resource: resource, + language: language, + cancellationToken: cancellationToken + ); + result = !isExists; + if (!result) + { + XException.Duplicate.Throw(); + } + + // + // Prepare Entity to Add ... + XStringDto item = new XStringDto + { + Language = language, + TranslatedValue = value, + ResourceTitle = resource, + }; + item = await AddAsync( + item: item, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + result = !item.IsNullOrDefault(); + + // + return result; + } + + /// + /// Update Resource ... + /// + /// + /// + /// + /// + /// + /// + public async Task Update( + string resource, + string value, + string language = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + bool result = false; + + // + // Normalize ... + if (language.IsNullOrEmpty()) + { + language = AppConfiguration.DefaultLanguage; + } + + // + // Validate ... + result = + !value.IsNullOrEmpty() && + !language.IsNullOrEmpty() && + !resource.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + // Check Value Must Exists ... + result = await IsResourceExists( + resource: resource, + language: language, + cancellationToken: cancellationToken + ); + if (!result) + { + XException.NotFound.Throw(); + } + + // + var item = await Get( + language: language, + resource: resource, + cancellationToken: cancellationToken + ); + item.TranslatedValue = value; + item = await UpdateAsync( + item: item, + id: item.Id, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + result = !item.IsNullOrDefault(); + + // + return result; + } + + /// + /// Add Or Update Resource ... + /// + /// + /// + /// + /// + /// + /// + public async Task AddOrUpdate( + string resource, + string value, + string language = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = false; + + // + // Normalize ... + if (language.IsNullOrEmpty()) + { + language = AppConfiguration.DefaultLanguage; + } + + // + // Check Existsance ... + bool isExists = await IsResourceExists( + resource: resource, + language: language + ); + + // + // Act Based on Existance ... + if (isExists) + { + // + result = await Update( + value: value, + resource: resource, + language: language, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + else + { + // + result = await Add( + value: value, + resource: resource, + language: language, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + return result; + } + + /// + /// Add Specified Locale Resource ... + /// + /// + /// + /// + /// + /// + public async Task AddByLocaleResource( + XLocaleResourceDto item, + string resource, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + bool isValid = + !item.IsNull() && + !resource.IsNullOrEmpty() && + !item.Value.IsNullOrEmpty() && + !item.Language.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Specified Resource / Language Exists ... + isValid = await IsResourceExists( + resource: resource, + language: item.Language, + cancellationToken: cancellationToken + ); + if (isValid) + { + XException.Duplicate.Throw(); + } + + // + // Prepare Item to Add ... + var result = new XStringDto + { + Language = item.Language, + ResourceTitle = resource, + TranslatedValue = item.Value + }; + result = await AddAsync( + item: result, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Update Specified Locale Resource ... + /// + /// + /// + /// + /// + /// + public async Task UpdateByLocaleResource( + XLocaleResourceDto item, + string resource, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + bool isValid = + !item.IsNull() && + !resource.IsNullOrEmpty() && + !item.Value.IsNullOrEmpty() && + !item.Language.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Specified Resource / Language Exists ... + isValid = await IsResourceExists( + resource: resource, + language: item.Language, + cancellationToken: cancellationToken + ); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + var result = await Get( + resource: resource, + language: item.Language, + cancellationToken: cancellationToken + ); + isValid = !result.IsNullOrDefault(); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Update ... + result.TranslatedValue = item.Value; + result = await UpdateAsync( + item: result, + id: result.Id, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + isValid = !result.IsNullOrDefault(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + + // + return result; + } + + /// + /// Add Or Update Resource By Locale ... + /// + /// + /// + /// + /// + /// + public async Task AddOrUpdateByLocaleResource( + XLocaleResourceDto item, + string resource, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + bool isValid = + !item.IsNull() && + !resource.IsNullOrEmpty() && + !item.Value.IsNullOrEmpty() && + !item.Language.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Specified Resource / Language Exists ... + isValid = await IsResourceExists( + resource: resource, + language: item.Language, + cancellationToken: cancellationToken + ); + + // + XStringDto result = null; + if (!isValid) + { + // + result = await AddByLocaleResource( + item: item, + resource: resource, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + else + { + // + result = await UpdateByLocaleResource( + item: item, + resource: resource, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + if (result.IsNullOrDefault()) + { + XException.ActionFailed.Throw(); + } + + // + return result; + } + + /// + /// Add Or Update all XResourceDto(s) Locales ... + /// + /// + /// + /// + /// + public async Task> AddByResource( + XResourceDto item, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + bool isValid = + !item.IsNull() && + item.Locales.HasChild() && + !item.Resource.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await Task + .WhenAll(item.Locales + .Select(async e => await AddOrUpdateByLocaleResource( + item: e, + resource: item.Resource, + connectionId: connectionId, + cancellationToken: cancellationToken))); + + // + return result; + } + #endregion + + // + #region Remove Actions ... + /// + /// Remove all Specified Language's Resources ... + /// + /// + /// + /// + /// + public async Task RemoveLanguageResources( + string language, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + bool isValid = !language.IsNullOrEmpty(); + if (!isValid) + { + return; + } + + // + var dtos = await Service + .FindManyAsync( + orderBuilder: null, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: e => + e.Language.ToNormalString() == language.ToNormalString()); + + // + try + { + // + await Service.RemoveRangeAsync( + items: dtos, + softDelete: false, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + var ids = dtos.Select(e => e.Id); + await SendPush( + connectionId: connectionId, + payLoad: ids.ToJSON(camelCase: true), + cancellationToken: cancellationToken, + action: XBaseEntityHubAction.DeleteMany.GetStringValue() + ); + } + catch (Exception ex) + { + throw ex; + } + } + + /// + /// Remove all Specified Resource Ids instances ... + /// + /// + /// + /// + /// + public async Task> RemoveResource( + string resource, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + bool isValid = !resource.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await Service + .FindManyAsync( + orderBuilder: null, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: e => + e.ResourceTitle.ToNormalString() == resource.ToNormalString()); + + // + try + { + // + await Service.RemoveRangeAsync( + items: result, + softDelete: false, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + var ids = result.Select(e => e.Id); + await SendPush( + connectionId: connectionId, + payLoad: ids.ToJSON(camelCase: true), + cancellationToken: cancellationToken, + action: XBaseEntityHubAction.DeleteMany.GetStringValue() + ); + } + catch (Exception ex) + { + throw ex; + } + + // + return result; + } + + /// + /// Remove Specified Resource ... + /// + /// + /// + /// + /// + /// + public async Task Remove( + string resource, + string language = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + bool result = false; + + // + // Normalize ... + if (language.IsNullOrEmpty()) + { + language = AppConfiguration.DefaultLanguage; + } + + // + // Validate ... + result = + !language.IsNullOrEmpty() && + !resource.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + // Check Resource Exists ... + result = await IsResourceExists( + language: language, + resource: resource, + cancellationToken: cancellationToken + ); + if (!result) + { + return result; + } + + // + var dto = await Get( + language: language, + resource: resource, + cancellationToken: cancellationToken + ); + result = !dto.IsNullOrDefault(); + if (!result) + { + return result; + } + + // + dto = await RemoveAsync( + id: dto.Id, + softDelete: false, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + result = !dto.IsNullOrDefault(); + + // + return result; + } + + /// + /// Remove all Resource of Specified XResourceDto ... + /// + /// + /// + /// + /// + public async Task RemoveByResource( + XResourceDto item, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + bool isValid = + !item.IsNullOrDefault() && + item.Locales.HasChild() && + !item.Resource.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + foreach (var locale in item.Locales) + { + // + await Remove( + resource: item.Resource, + language: locale.Language, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Entities\XStringEFRepository.cs + +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Providers; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Entities +{ + public class XStringEFRepository : XBaseEFRepository, IXStringRepository + where TContext : XDbContext + { + public XStringEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXStringKeyGenerator keyGenerator = null, + IXStringRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Entities\XStringEntityHub.cs + +using Microsoft.Extensions.Logging; +using xPushService.Base; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Entities +{ + public class XStringEntityHub : XBaseEntityHub, IXStringEntityHub + { + public XStringEntityHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Entities\XStringInMemoryRepository.cs + +using xDataService.Configuration; +using xDataService.Providers; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Entities +{ + public class XStringInMemoryRepository : XBaseInMemoryRepository, IXStringRepository + { + public XStringInMemoryRepository( + XDataServiceConfiguration configuration, + IXStringKeyGenerator keyGenerator = null, + IXStringRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Entities\XStringKeyGenerator.cs + +using xDataService.Providers; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Entities +{ + public class XStringKeyGenerator : XIntKeyGenerator, IXStringKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Entities\XStringMongoRepository.cs + +using xDataService.Configuration; +using xDataService.Providers; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Entities +{ + public class XStringMongoRepository : XBaseMongoRepository, IXStringRepository + { + public XStringMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXStringKeyGenerator keyGenerator = null, + IXStringRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Entities\XStringRepositoryEvents.cs + +using xDataService.Providers; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Entities +{ + public class XStringRepositoryEvents : XBaseRepositoryEvents, IXStringRepositoryEvents + {} +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Entities\XStringRepositoryProvider.cs + +using Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Entities +{ + public class XStringRepositoryProvider : XBaseEntityProvider, IXStringRepositoryProvider + { + public XStringRepositoryProvider( + IHubContext hub, + IXStringRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + repository, + dataConfiguration, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\Entities\XStringSeederBase.cs + +using xDataService.Configuration; +using xDataService.Providers; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Providers.Entities +{ + public abstract class XStringSeederBase : XBaseDbSeeder, IXStringSeeder + { + protected XStringSeederBase( + string entity, + string database, + IXStringRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + entity, + database, + repository, + dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\GraphQL\XStringGraphQLTypeHelper.cs + +using xDataService.Configuration; +using xDataService.GraphQL; +using xStringService.Constants; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Providers.GraphQL +{ + public class XStringGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXStringGraphQLTypeHelper + { + public XStringGraphQLTypeHelper( + XDataServiceConfiguration configuration + ) : base(configuration) + { } + + public override string GetInQueryCollectionName() + { + return XStringServiceConstants.XStringCollectionName; + } + + public override string GetInQuerySingleName() + { + return XStringServiceConstants.XStringSingleName; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\GraphQL\XStringGraphQuery.cs + +using GraphQL.Types; +using xDataService.Configuration; +using xDataService.GraphQL; +using xStringService.Interfaces.Entities; +using xStringService.Models.Entities; + +namespace xDataHelper.Providers.GraphQL +{ + public class XStringGraphQuery : XBaseGraphQLQuery + { + public XStringGraphQuery( + XDataServiceConfiguration configuration, + IXStringRepository repository, + IXStringGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\GraphQL\XStringGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xDataHelper.Providers.GraphQL +{ + public class XStringGraphSchema : Schema + { + public XStringGraphSchema(IServiceProvider services) : base(services) + { + Query = (XStringGraphQuery)services.GetService(typeof(XStringGraphQuery)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\GraphQL\XStringGraphType.cs + +using xDataService.GraphQL; +using xStringService.Models.Entities; + +namespace xDataHelper.Providers.GraphQL +{ + public class XStringGraphType : XBaseGraphObjectType + { + public XStringGraphType() : base() + { + Field(x => x.Language); + Field(x => x.ResourceTitle); + Field(x => x.TranslatedValue); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\XBaseItemResourceProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Constants; +using xModels.Dtos; +using xStringService.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Models.Dtos; + +namespace xStringService.Providers +{ + public abstract class XBaseItemResourceProvider : IXItemResourceProvider + { + // + #region Props ... + /// + /// Item Name for Resource Providing ... + /// + private readonly string item; + + /// + /// an Implementation of Item Resource Provider + /// + /// + private readonly XBaseResourceProvider provider; + + /// + /// String Provider Service ... + /// + private readonly IXStringServiceProvider stringProvider; + #endregion + + // + #region Constructor ... + protected XBaseItemResourceProvider( + string item, + IXStringServiceProvider stringProvider + ) + { + // + this.item = item; + this.stringProvider = stringProvider; + provider = new XBaseResourceProvider( + item, + stringProvider + ); + } + #endregion + + // + #region Group Actions ... + /// + /// Retrieve a List of Exists Languages for Current Item ... + /// + /// + /// + public virtual async Task> GetLanguages( + CancellationToken cancellationToken = default + ) + { + // + var result = (await ExtractItems(cancellationToken)) + .Select(x => x.Language) + .Distinct(); + + // + return result; + } + + /// + /// Retrieve all Exists Items of type ... + /// + /// + /// + public virtual async Task> GetResourceLocales( + CancellationToken cancellationToken = default + ) + { + // + var result = await ExtractItems(cancellationToken); + + // + return result; + } + + /// + /// Remove all Exists Locales which Related to this Provider ... + /// + /// + /// + /// + public virtual async Task RemoveResourceLocales( + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var isDelete = false; + XStringDto deleted = null; + var items = await ExtractItems(cancellationToken); + foreach (var item in items) + { + // + // Check Cancellation ... + if (cancellationToken.IsCancellationRequested) + { + break; + } + + // + // Remove Item ... + deleted = await stringProvider.RemoveAsync( + id: item.Id, + softDelete: false, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + // Check Removation Succeed ... + isDelete = !deleted.IsNullOrDefault(); + if (!isDelete) + { + XException.ActionFailed.Throw(); + } + } + } + #endregion + + // + #region Item Actions ... + /// + /// Get all Exists Items for Specified identitifer ... + /// + /// + /// + /// + public virtual async Task> GetResourceLocales( + object identifier, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = !identifier.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Setting Resource Title ... + SetResourceTitle(identifier); + + // + // Retrieve Resource List ... + var result = await provider + .GetResourceLocales(cancellationToken); + + // + return result; + } + + /// + /// Get all Exists Items for Specified identitifer ... + /// + /// + /// + /// + public virtual async Task> GetLocales( + object identifier, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = !identifier.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Setting Resource Title ... + SetResourceTitle(identifier); + + // + // Retrieve Resource List ... + var result = await provider + .GetLocales(cancellationToken); + + // + return result; + } + + /// + /// Get all Exists Locales Representaion based on Specified Identitifer + /// as XResourceDto ... + /// + /// + /// + /// + public virtual async Task GetResource( + object identifier, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = !identifier.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Setting Resource Title ... + SetResourceTitle(identifier); + + // + // Retrieve Resource List ... + var result = await provider + .GetResource(cancellationToken); + + // + return result; + } + + /// + /// Add Or Update Locale for Specified Identitifer ... + /// + /// + /// + /// + /// + /// + public virtual async Task AddOrUpdateLocale( + object identifier, + XLocaleResourceDto locale, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !identifier.IsNull() && + !locale.IsNullOrDefault() && + !locale.Value.IsNullOrEmpty() && + !locale.Language.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Setting Resource Title ... + SetResourceTitle(identifier); + + // + // Retrieve Resource List ... + var result = await provider + .AddOrUpdateLocale( + locale: locale, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Update a Resource State ... + /// + /// + /// + /// + /// + /// + public virtual async Task AddOrUpdateResource( + object identifier, + XResourceDto resource, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !identifier.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Identified Resource Title ... + var identifiedResource = GetResourceTitle(identifier); + isValid = + !resource.IsNullOrDefault() && + (resource.Resource.IsNullOrEmpty() || + resource.Resource == identifiedResource); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + if (resource.Resource.IsNullOrEmpty()) + { + resource.Resource = identifiedResource; + } + + // + // Setting Resource Title ... + SetResourceTitle(identifier); + + // + // Retrieve Resource List ... + var result = await provider + .AddOrUpdateResource( + resource: resource, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Remove Specified Language Locale of Specified Identifier ... + /// + /// + /// + /// + /// + /// + public virtual async Task RemoveLocale( + object identifier, + string language, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !identifier.IsNull() && + !language.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Locale Exists ... + isValid = await HasLocale( + language: language, + identifier: identifier, + cancellationToken: cancellationToken + ); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + var locale = (await GetLocales( + identifier: identifier, + cancellationToken: cancellationToken + )) + .FirstOrDefault(x => + x.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase)); + isValid = !locale.IsNullOrDefault(); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Setting Resource Title ... + SetResourceTitle(identifier); + + // + var result = await provider.RemoveLocale( + locale: locale, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Remove all Exists Locales for Specified Identifier ... + /// + /// + /// + /// + /// + public virtual async Task RemoveLocales( + object identifier, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !identifier.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var resource = await GetResource( + identifier: identifier, + cancellationToken: cancellationToken + ); + isValid = !resource.IsNullOrDefault(); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Setting Resource Title ... + SetResourceTitle(identifier); + + // + await provider.RemoveResource( + resource: resource, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + /// + /// Check Specified ocale Exists for Specified Identifier ... + /// + /// + /// + /// + /// + public virtual async Task HasLocale( + object identifier, + string language, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !identifier.IsNull() && + !language.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Setting Resource Title ... + SetResourceTitle(identifier); + + // + var result = await provider.HasLocale( + language: language, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Get all Exists Languages for Specified Identifier ... + /// + /// + /// + /// + public virtual async Task> GetLanguages( + object identifier, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = !identifier.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Setting Resource Title ... + SetResourceTitle(identifier); + + // + var result = await provider + .GetLanguages(cancellationToken); + + // + return result; + } + #endregion + + // + #region Resource Title Action ... + /// + /// Retrieve Resource Title ... + /// + /// + /// + public string GetResourceTitle(object identifier = null) + { + // + var result = item; + + // + if (!identifier.IsNull()) + { + result = $"{item}_{identifier}"; + } + + // + return result; + } + #endregion + + // + #region Private ... + /// + /// Set Resource Title ... + /// + /// + private void SetResourceTitle(object identifier = null) + { + // + var itemIdentifier = GetResourceTitle(identifier); + provider.SetField( + field: "resource", + value: $"{itemIdentifier}" + ); + } + + /// + /// xtract all Items which Belongs to Provider ... + /// + /// + /// + private async Task> ExtractItems( + CancellationToken cancellationToken = default + ) + { + // + var result = await stringProvider + .FindManyAsync( + includeBuilder: null, + cancellationToken: cancellationToken, + orderBuilder: x => x.OrderBy(y => y.Id), + predicate: x => + x.ResourceTitle.StartsWith( + item, + StringComparison.InvariantCultureIgnoreCase) + ); + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\XBaseResourceProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Constants; +using xModels.Dtos; +using xStringService.Extensions; +using xStringService.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Models.Dtos; + +namespace xStringService.Providers +{ + public class XBaseResourceProvider : IXResourceProvider + { + protected string resource; + private readonly IXStringServiceProvider provider; + + public XBaseResourceProvider( + string resource, + IXStringServiceProvider provider + ) + { + this.resource = resource; + this.provider = provider; + } + + // + #region Actions ... + /// + /// Retrieve a List of Exists Languages ... + /// + /// + /// + public virtual async Task> GetLanguages( + CancellationToken cancellationToken = default + ) + { + // + var result = (await ExtractItems(cancellationToken)) + .Select(x => x.Language) + .Distinct() + .ToList(); + + // + return result; + } + + /// + /// Retrieve all Exists Items ... + /// + /// Specified Resource Title + /// + /// + public virtual async Task> GetResourceLocales( + CancellationToken cancellationToken = default + ) + { + // + var result = await ExtractItems(cancellationToken); + + // + return result; + } + + /// + /// Extract Translation of in Specified Language ... + /// + /// Specified Language ... + /// + /// + public virtual async Task GetResourceTranslation( + string language, + CancellationToken cancellationToken = default + ) + { + // + var item = (await ExtractItems(cancellationToken)) + .FirstOrDefault(x => x.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase)); + + // + var result = item?.TranslatedValue ?? string.Empty; + + // + return result; + } + + /// + /// Check Has Specified Translation or not ... + /// + /// Specified Language ... + /// + /// + public virtual async Task HasLocale( + string language, + CancellationToken cancellationToken = default + ) + { + // + var item = (await ExtractItems(cancellationToken)) + .FirstOrDefault(x => x.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase)); + + // + var result = !item.IsNullOrDefault(); + + // + return result; + } + + /// + /// Retrieve as Locale Model ... + /// + /// Specified Language ... + /// + /// + public virtual async Task GetLocale( + string language, + CancellationToken cancellationToken = default + ) + { + // + var item = (await ExtractItems(cancellationToken)) + .FirstOrDefault(x => x.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase)); + + // + var result = + !item.IsNullOrDefault() + ? item.ToXLocaleResourceDto() + : null; + + // + return result; + } + + /// + /// Retrieve all Exists Locale Models ... + /// + /// + /// + public virtual async Task> GetLocales( + CancellationToken cancellationToken = default + ) + { + // + var result = (await ExtractItems(cancellationToken)) + .Select(x => x.ToXLocaleResourceDto()) + .ToList(); + + // + return result; + } + + /// + /// Retrieve as XResourceDto ... + /// + /// + /// + /// + public virtual async Task GetResource( + CancellationToken cancellationToken = default + ) + { + // + var locales = await GetLocales(); + + // + var result = new XResourceDto + { + Locales = locales, + Resource = resource, + }; + + // + return result; + } + + /// + /// Add or Update a Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + /// + public virtual async Task AddOrUpdateResource( + XResourceDto resource, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var isValid = + !resource.IsNullOrDefault() && + resource.Resource == this.resource; + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Exists Resource ... + var result = await GetResource(cancellationToken); + isValid = !result.IsNullOrDefault() && result.Locales.HasChild(); + if (isValid) + { + await RemoveResource( + resource: result, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + foreach (var locale in resource.Locales) + { + // + var item = new XStringDto + { + Language = locale.Language, + ResourceTitle = this.resource, + TranslatedValue = locale.Value, + }; + + // + await provider.AddAsync( + item: item, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + result = await GetResource(cancellationToken); + return result; + } + + /// + /// Add Or Update Locale ... + /// + /// + /// + /// + public virtual async Task AddOrUpdateLocale( + XLocaleResourceDto locale, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var item = (await ExtractItems(cancellationToken)) + .FirstOrDefault(x => + x.ResourceTitle == resource && + x.Language == locale.Language); + var isExists = !item.IsNullOrDefault(); + if (isExists) + { + item.TranslatedValue = locale.Value; + item = await provider.UpdateAsync( + item: item, + id: item.Id, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + else + { + item = new XStringDto + { + ResourceTitle = resource, + Language = locale.Language, + TranslatedValue = locale.Value + }; + item = await provider.AddAsync( + item: item, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + var result = await GetResource(cancellationToken); + + // + return result; + } + + /// + /// Remove Locale ... + /// + /// + /// + /// + /// + public virtual async Task RemoveLocale( + XLocaleResourceDto locale, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var item = (await ExtractItems(cancellationToken)) + .FirstOrDefault(x => + x.ResourceTitle == resource && + x.Language == locale.Language); + var isExists = !item.IsNullOrDefault(); + if (isExists) + { + await provider.RemoveAsync( + id: item.Id, + softDelete: false, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + var result = await GetResource(cancellationToken); + + // + return result; + } + + /// + /// Remove Specified Resource Model ... + /// + /// a XResourceDto Model ... + /// + /// + /// + public virtual async Task RemoveResource( + XResourceDto resource, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate Resource ... + var isValid = + !resource.IsNullOrDefault() && + !resource.Resource.IsNullOrEmpty() && + resource.Resource == this.resource; + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var items = await ExtractItems(cancellationToken); + + // + foreach (var item in items) + { + // + await provider.RemoveAsync( + id: item.Id, + softDelete: false, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + } + #endregion + + // + #region Private ... + private async Task> ExtractItems( + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.FindManyAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + orderBuilder: x => x + .OrderBy(y => y.Language) + .ThenBy(y => y.ResourceTitle), + cancellationToken: cancellationToken, + predicate: x => + x.ResourceTitle.Equals(resource, StringComparison.InvariantCultureIgnoreCase) + ); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\XStringEntityRegisterar.cs + +using Microsoft.EntityFrameworkCore; +using xDataService.Interfaces; +using xDataService.Providers; +using xStringService.Configurations.Entities; +using xStringService.Models.Entities; + +namespace xStringService.Providers +{ + /// + /// an Entity Registerar class for XStringService ... + /// + public class XStringEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar + { + public XStringEntityRegisterar() + : base(nameof(XStringEntityRegisterar)) + { + // + // Add XString Entity ... + AddEntity(); + } + + /// + /// Configure Entities ... + /// + /// + public override void ConfigureEntities(ModelBuilder modelBuilder) + { + // + // Configure Entity Using Provided ... + modelBuilder + .ApplyConfigurationsFromAssembly(typeof(XStringEntityConfiguration).Assembly); + + // + base.ConfigureEntities(modelBuilder); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xStringService\Providers\XStringProvider.cs + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Models; +using xModels.Dtos; +using xPushService.Providers; +using xStringService.Extensions; +using xStringService.Interfaces; +using xStringService.Interfaces.Dtos; +using xStringService.Models.Dtos; +using xStringService.Models.Entities; +using xStringService.Providers.Dtos; + +namespace xStringService.Providers +{ + public class XStringProvider : XBaseProvided, IXStringProvider + { + public XStringProvider( + IXStringServiceProvider provider + ) : base( + provider, + permittedRoles: new string[] { } + ) + { } + + public override bool IsOwned( + XStringDto item, + XUserClaimsInfoDto userInfo + ) + { + return !item.IsNullOrDefault(); + } + + public override bool IsOwned( + XString item, + XUserClaimsInfoDto userInfo + ) + { + return !item.IsNullOrDefault(); + } + + // + #region Tools ... + /// + /// Retrieve a List of Exists Languages ... + /// + /// + /// + public async Task> GetLanguages( + CancellationToken cancellationToken = default + ) + { + // + var result = (await GetAll( + includeBuilder: null, + cancellationToken: cancellationToken, + orderBuilder: x => x.OrderBy(y => y.Language) + )) + .Select(x => x.Language) + .Distinct() + .ToList(); + + // + return result; + } + + /// + /// Retrieve a List of Resource Titles ... + /// + /// if provided, returns all language Specified resource titles, if not, all Resource Titles + /// + /// + public async Task> GetResourceTitles( + string language = null, + CancellationToken cancellationToken = default + ) + { + // + var result = (await FindMany( + includeBuilder: null, + cancellationToken: cancellationToken, + orderBuilder: x => x.OrderBy(y => y.ResourceTitle), + predicate: x => + string.IsNullOrWhiteSpace(language) || + x.Language.Equals(language, System.StringComparison.InvariantCultureIgnoreCase) + )) + .Select(x => x.ResourceTitle) + .Distinct() + .ToList(); + + // + return result; + } + + /// + /// Retrieve all Exists Items which belongs to Specified Resource Title ... + /// + /// Specified Resource Title + /// + /// + public async Task> GetResourceLocales( + string resource, + CancellationToken cancellationToken = default + ) + { + // + var result = await FindMany( + includeBuilder: null, + cancellationToken: cancellationToken, + orderBuilder: x => x.OrderBy(y => y.Language), + predicate: x => + !string.IsNullOrWhiteSpace(resource) && + resource.Equals(x.ResourceTitle, System.StringComparison.InvariantCultureIgnoreCase) + ); + + // + return result; + } + + /// + /// Extract Translation of a Resource in Specified Language ... + /// + /// Specified Language ... + /// Specified Resource Title ... + /// + /// + public async Task GetResourceTranslation( + string language, + string resource, + CancellationToken cancellationToken = default + ) + { + // + var result = (await FindOne( + includeBuilder: null, + cancellationToken: cancellationToken, + predicate: x => + !string.IsNullOrWhiteSpace(language) && + !string.IsNullOrWhiteSpace(resource) && + x.Language.Equals(language, System.StringComparison.InvariantCultureIgnoreCase) && + x.ResourceTitle.Equals(resource, System.StringComparison.InvariantCultureIgnoreCase) + ))? + .TranslatedValue ?? ""; + + // + return result; + } + + /// + /// Retrieve a Resource as Locale Model ... + /// + /// Specified Language ... + /// Specified Resource Title ... + /// + /// + public async Task GetLocale( + string language, + string resource, + CancellationToken cancellationToken = default + ) + { + // + var result = (await FindOne( + includeBuilder: null, + cancellationToken: cancellationToken, + predicate: x => + !string.IsNullOrWhiteSpace(language) && + !string.IsNullOrWhiteSpace(resource) && + x.Language.Equals(language, System.StringComparison.InvariantCultureIgnoreCase) && + x.ResourceTitle.Equals(resource, System.StringComparison.InvariantCultureIgnoreCase) + ))? + .ToXLocaleResourceDto() ?? null; + + // + return result; + } + + /// + /// Retrieve all Exists Resource Instances as Locale Models ... + /// + /// + /// + /// + public async Task> GetLocales( + string resource, + CancellationToken cancellationToken = default + ) + { + // + var result = (await GetResourceLocales( + resource: resource, + cancellationToken: cancellationToken + )) + .ToList() + .Select(x => x.ToXLocaleResourceDto()) + .ToList(); + + // + return result; + } + + /// + /// Retrieve all Locales of Specified Resource and Represent as XResourceDto ... + /// + /// + /// + /// + public async Task GetResource( + string resource, + CancellationToken cancellationToken = default + ) + { + // + XResourceDto result = null; + + // + // Validate Resource ... + if (resource.IsNullOrEmpty()) + { + return result; + } + + // + // Extract Locales of Resource ... + var locales = await GetLocales( + resource: resource, + cancellationToken: cancellationToken + ); + + // + // Prepare Result ... + result = new XResourceDto + { + Locales = locales, + Resource = resource, + }; + + // + return result; + } + + /// + /// Add or Update a Resource Model ... + /// + /// a XResourceDto Model ... + /// Actor User for Permissions ... + /// + /// + /// + public async Task AddOrUpdateResource( + XResourceDto resource, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + XResourceDto result = null; + + // + // Validate Args ... + var isValid = + !resource.IsNullOrDefault() && + !resource.Resource.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Permissions ... + isValid = + userInfo.IsNullOrDefault() || + HasPermision(userInfo); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + // Remove all Resources if Exists ... + result = await GetResource( + resource: resource.Resource, + cancellationToken: cancellationToken + ); + var isExists = !result.IsNullOrDefault(); + if (isExists) + { + // + await RemoveResource( + resource: result, + userInfo: userInfo, + cancellationToken: cancellationToken + ); + } + + // + // Loop through all Exists Locales and Add them ... + foreach (var locale in resource.Locales) + { + // + if (cancellationToken.IsCancellationRequested) + { + break; + } + + // + var item = new XStringDto + { + Language = locale.Language, + TranslatedValue = locale.Value, + ResourceTitle = resource.Resource, + }; + await Add( + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + // Reload Result after Updates ... + result = await GetResource( + resource: resource.Resource, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Remove Specified Resource Model ... + /// + /// a XResourceDto Model ... + /// Actor User for Permissions ... + /// + /// + /// + public async Task RemoveResource( + XResourceDto resource, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = !resource.IsNullOrDefault(); + if (!isValid) + { + return; + } + + // + var items = await GetResourceLocales( + resource: resource.Resource, + cancellationToken: cancellationToken + ); + isValid = + !items.IsNullOrDefault() && + items.HasChild(); + if (!isValid) + { + return; + } + + // + foreach (var item in items) + { + // + if (cancellationToken.IsCancellationRequested) + { + break; + } + + // + await Remove( + id: item.Id, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Configurations\Entities\XTagEntityConfiguration.cs + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xDataService.Providers; +using xTagService.Models.Entities; + +namespace xTagService.Configurations.Entities +{ + public class XTagEntityConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Tags"); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Constants\XTagServiceConstants.cs + +namespace xTagService.Constants +{ + public struct XTagServiceConstants + { + // + // Service Extensions Log Tag ... + public const string XTagServiceDILogTag = "XTagService"; + + // + // Table Mapping Identifiers ... + public const string XTagTable = "Tags"; + + // + // GraphQL Collection Mappings ... + public const string XTagSingleName = "tag"; + public const string XTagCollectionName = "tags"; + + // + // Hubs ... + public const string XTagDtoHub = "tagDto"; + public const string XTagEntityHub = "tagEntity"; + + // + // Custome Constants ... + + // + // EF Repositories Helper Extensions ... + public const string GetXTagEFRepositoryDescriptor = "GetXTagEFRepositoryDescriptor"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Controllers\XBaseProvidedHasTagControllerBase.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; +using xModels.Base; +using xModels.Dtos; +using xPushService.Base; +using xPushService.Interfaces; +using xTagService.Interfaces; + +namespace xTagService.Controllers +{ + [AllowAnonymous] + public abstract class XBaseProvidedHasTagControllerBase : XBaseIdentityApiV1Controller, IXBaseProvidedHasTagController + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + private readonly IXBaseProvided provider; + private readonly IXTagProvided tagProvided; + private readonly Func, IOrderedQueryable> defaultOrderBuilder; + private readonly Func, IIncludableQueryable> defaultIncludeBuilder; + + protected XBaseProvidedHasTagControllerBase( + ILogger> logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXBaseProvided provider, + IXTagProvided tagProvided, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { + this.provider = provider; + this.tagProvided = tagProvided; + this.defaultOrderBuilder = defaultOrderBuilder; + this.defaultIncludeBuilder = defaultIncludeBuilder; + } + + // + #region Provided ... + // + #region Retrieve ... + /// + /// Get Specified Item by ID ... + /// + /// + /// + /// + /// + [HttpGet("{id}")] + public virtual async Task> Get( + [FromRoute] TKey id, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(id); + + // + var result = await provider.Get( + id: id, + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get All Exists Items ... + /// + /// + /// + /// + /// + [HttpGet("")] + public virtual async Task>> GetAll( + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await provider.GetAll( + orderBuilder: !useDefaultOrders + ? null + : defaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find an item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + [HttpGet("Find/One")] + public virtual async Task> FindOne( + [FromRoute] string query, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(query) + .ValidateGroupAsync(); + + // + var result = await provider.FindOne( + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken, + predicate: x => x.PropValuesContains(query) + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find a collection of Items by proving a Query Search ... + /// + /// + /// + /// + /// + /// + [HttpGet("Find/Many")] + public virtual async Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(query) + .ValidateGroupAsync(); + + // + var result = await provider.FindMany( + orderBuilder: !useDefaultOrders + ? null + : defaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken, + predicate: x => x.PropValuesContains(query) + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + public virtual async Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(query) + .ValidateGroupAsync(); + + // + var result = await provider.Query( + query: query, + predicate: null, + orderBuilder: !useDefaultOrders + ? null + : defaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Owned Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + [HttpGet("Query/Owned")] + public virtual async Task>> QueryOwned( + [FromQuery] XQuery query, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(query) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + + // + var result = await provider.QueryOwned( + query: query, + predicate: null, + userInfo: userInfo, + orderBuilder: !useDefaultOrders + ? null + : defaultOrderBuilder, + includeBuilder: !useDefaultIncludes + ? null + : defaultIncludeBuilder, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Add/Update/Remove ... + /// + /// Add an item ... + /// + /// + /// + /// + [HttpPost("")] + public virtual async Task> Add( + [FromBody] TDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(item) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Add( + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update an item values ... + /// + /// + /// + /// + /// + [HttpPut("{id}")] + public virtual async Task> Update( + [FromRoute] TKey id, + [FromBody] TDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotNull(item) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Update( + id: id, + item: item, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove an Item ... + /// + /// + /// + /// + [HttpDelete("{id}")] + public virtual async Task> Remove( + [FromRoute] TKey id, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + var result = await provider.Remove( + id: id, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + #endregion + + // + #region Tag ... + /// + /// Attach Tag to Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/attach")] + public virtual async Task AttachTag( + [FromRoute] TKey id, + [FromBody] XBaseValueContainer tag, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotNull(tag) + .AddNotEmpty(tag.Value) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + // Get Result ... + await tagProvided + .AttachTag( + id: id, + tag: tag.Value, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Detach Tag of Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/detach")] + public virtual async Task DetachTag( + [FromRoute] TKey id, + [FromBody] XBaseValueContainer tag, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotNull(tag) + .AddNotEmpty(tag.Value) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + // Get Result ... + await tagProvided + .DetachTag( + id: id, + tag: tag.Value, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Attach Tags to Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/attachs")] + public virtual async Task AttachTags( + [FromRoute] TKey id, + [FromBody] XBaseRangeRequest tags, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotNull(tags) + .AddNotZeroChilds(tags.Items) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + // Get Result ... + await tagProvided + .AttachTags( + id: id, + tags: tags.Items, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Detach Tags of Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/detachs")] + public virtual async Task DetachTags( + [FromRoute] TKey id, + [FromBody] XBaseRangeRequest tags = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + // Get Result ... + if (!tags.IsNull()) + { + // + await tagProvided + .DetachTags( + id: id, + tags: tags.Items, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + else + { + // + await tagProvided + .DetachTags( + id: id, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get Specified Item Tags ... + /// + /// + /// + /// + [HttpGet("{id}/tags")] + public virtual async Task>> GetTags( + [FromRoute] TKey id, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .ValidateGroupAsync(); + + // + // Get Result ... + var result = await tagProvided.GetTags( + id: id, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Controllers\XTagProvidedControllerBase.cs + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; +using xModels.Base; +using xPushService.Base; +using xTagService.Interfaces; + +namespace xTagService.Controllers +{ + public abstract class XTagProvidedControllerBase : XBaseIdentityApiV1Controller, IXTagProvidedController + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + private readonly IXTagProvided tagProvided; + + protected XTagProvidedControllerBase( + ILogger> logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXIdentityProvider identityProvider, + IXTagProvided tagProvided + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { + this.tagProvided = tagProvided; + } + + /// + /// Attach Tag to Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/attach")] + public virtual async Task AttachTag( + [FromRoute] TKey id, + [FromBody] XBaseValueContainer tag, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotNull(tag) + .AddNotEmpty(tag.Value) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + // Get Result ... + await tagProvided + .AttachTag( + id: id, + tag: tag.Value, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Detach Tag of Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/detach")] + public virtual async Task DetachTag( + [FromRoute] TKey id, + [FromBody] XBaseValueContainer tag, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotNull(tag) + .AddNotEmpty(tag.Value) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + // Get Result ... + await tagProvided + .DetachTag( + id: id, + tag: tag.Value, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Attach Tags to Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/attachs")] + public virtual async Task AttachTags( + [FromRoute] TKey id, + [FromBody] XBaseRangeRequest tags, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .AddNotNull(tags) + .AddNotZeroChilds(tags.Items) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + // Get Result ... + await tagProvided + .AttachTags( + id: id, + tags: tags.Items, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Detach Tags of Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/detachs")] + public virtual async Task DetachTags( + [FromRoute] TKey id, + [FromBody] XBaseRangeRequest tags = null, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .ValidateGroupAsync(); + + // + var userInfo = await GetUserInfo(); + var connectionId = GetConnectionId(); + + // + // Get Result ... + if (!tags.IsNull()) + { + // + await tagProvided + .DetachTags( + id: id, + tags: tags.Items, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + else + { + // + await tagProvided + .DetachTags( + id: id, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get Specified Item Tags ... + /// + /// + /// + /// + [HttpGet("{id}/tags")] + public virtual async Task>> GetTags( + [FromRoute] TKey id, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id) + .ValidateGroupAsync(); + + // + // Get Result ... + var result = await tagProvided.GetTags( + id: id, + cancellationToken: cancellationToken + ); + + // + return Ok(result.ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Controllers\XTagProviderControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xTagService.Interfaces; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; +using xTagService.Providers.Dtos; + +namespace xTagService.Controllers +{ + public abstract class XTagProviderControllerBase : XBaseProvidedController, IXTagProviderController + { + protected XTagProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXTagProvider 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\xTagService\Controllers\XTagRepositoryControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xDataService.Controllers; +using xDataService.Interfaces; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Controllers +{ + public abstract class XTagRepositoryControllerBase : XBaseRepositoryController, IXBaseRepositoryController + { + protected XTagRepositoryControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXTagRepository repository, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repository, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Controllers\XTagRepositoryProviderControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xPushService.Interfaces; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; +using xTagService.Providers.Entities; + +namespace xTagService.Controllers +{ + public abstract class XTagRepositoryProviderControllerBase : XBaseRepositoryProviderController, IXBaseRepositoryProviderController + { + protected XTagRepositoryProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXTagRepositoryProvider 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\xTagService\Controllers\XTagServiceControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xDataService.Controllers; +using xDataService.Interfaces; +using xTagService.Interfaces.Dtos; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; + +namespace xTagService.Controllers +{ + public abstract class XTagServiceControllerBase : XBaseServiceController, IXBaseServiceController + { + protected XTagServiceControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXTagRepositoryService repositoryService, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider, + repositoryService, + defaultOrderBuilder, + defaultIncludeBuilder + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Controllers\XTagServiceProviderControllerBase.cs + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xPushService.Controllers; +using xPushService.Interfaces; +using xTagService.Interfaces.Dtos; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; +using xTagService.Providers.Dtos; + +namespace xTagService.Controllers +{ + public abstract class XTagServiceProviderControllerBase : XBaseServiceProviderController, IXBaseServiceProviderController + { + protected XTagServiceProviderControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXTagServiceProvider 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\xTagService\DI\XDIHelperExtension.cs + +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xCommons.Helpers; +using xDataService.Constants; +using xDataService.Db; +using xDataService.DI; +using xDataService.Models; +using xExceptions.Constants; +using xPushService.DI; +using xPushService.Helpers; +using xTagService.Constants; +using xTagService.Helpers; +using xTagService.Interfaces; +using xTagService.Interfaces.Dtos; +using xTagService.Interfaces.Entities; +using xTagService.Providers; +using xTagService.Providers.Dtos; +using xTagService.Providers.Entities; + +namespace xTagService.DI +{ + public static class XDIHelperExtension + { + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXTagService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + AddTagService( + contextType: null, + services: services, + lifeTime: lifeTime, + repositoryType: repositoryType + ); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + public static void AddXTagService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + where TContext : XDbContext + { + AddTagService( + services: services, + lifeTime: lifeTime, + contextType: typeof(TContext), + repositoryType: repositoryType + ); + } + + /// + /// Use XTagService Middleware ... + /// + /// + /// + public static void UseXTagService( + this IApplicationBuilder app, + bool isDevelopmentEnvironment = false + ) + { + // + #region Using XTag Hubs ... + // + var pushHelper = new XPushServiceHelper(); + + // + pushHelper.AddHub(XTagServiceConstants.XTagDtoHub); + pushHelper.AddHub(XTagServiceConstants.XTagEntityHub); + + // + app.UseXPushService(pushHelper); + #endregion + + // + // Using XTag 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 = XTagServiceConstants.XTagServiceDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + private static void AddTagService( + 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(XTagServiceHelper) + .InvokeGenericMethod( + args: null, + runtimeType: contextType, + methodName: XTagServiceConstants.GetXTagEFRepositoryDescriptor + ); + break; + + // + case XRepositoryType.Mongo: + descriptor = XTagServiceHelper.GetXTagMongoRepositoryDescriptor(); + break; + + // + case XRepositoryType.InMemory: + descriptor = XTagServiceHelper.GetXTagInMemoryRepositoryDescriptor(); + break; + } + + // + // Register XTag Repository Descriptor ... + services.AddXRepository( + lifeTime: lifeTime, + descriptor: descriptor + ); + + // + // Register Dto Services ... + services.Add( + new ServiceDescriptor(typeof(IXTagServiceProvider), typeof(XTagServiceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXTagRepositoryService), typeof(XTagRepositoryService), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXTagRepositoryProvider), typeof(XTagRepositoryProvider), lifeTime) + ); + + // + services.Add( + new ServiceDescriptor(typeof(IXTagProvider), typeof(XTagProvider), lifeTime) + ); + + // + Log("Service Regitration Succeed ..."); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Extensions\XModelExtensions.cs + +using System.Collections.Generic; +using System.Linq; +using xCommons.Extensions; +using xDataService.Extensions; +using xDataService.Models; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; + +namespace xTagService.Extensions +{ + public static class XModelExtensions + { + /// + /// Extract References of a Model ... + /// + /// + /// + public static IList> GetReferences( + this XTag 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 XTagDto 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 XTag UpdateReferences( + this XTag source, + IList> references, + bool forceClean = true + ) + { + // + XTag 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 XTagDto UpdateReferences( + this XTagDto source, + IList> references, + bool forceClean = true + ) + { + // + XTagDto 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\xTagService\Helpers\XTagServiceHelper.cs + +using GraphQL.Types; +using xDataService.Db; +using xDataService.Models; +using xDataService.Providers; +using xTagService.Interfaces; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; +using xTagService.Providers.Entities; +using xTagService.Providers.GraphQL; + +namespace xTagService.Helpers +{ + public static class XTagServiceHelper + { + // + #region EF Repository Descriptor Provider(s) ... + public static XEFRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagEFRepository, + TContext + > GetXTagEFRepositoryDescriptor() + where TContext : XDbContext + { + // + var result = new XEFRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagEFRepository, + TContext + >(); + + // + return result; + } + + public static XEFRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagEFRepository, + TContext, + IXTagSeeder, + TSeederImplementation + > GetXTagEFRepositoryDescriptor() + where TContext : XDbContext + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XEFRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagEFRepository, + TContext, + IXTagSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region Mongo Repository Descriptor Provider(s) ... + public static XMongoRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagMongoRepository + > GetXTagMongoRepositoryDescriptor() + { + // + var result = new XMongoRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagMongoRepository + >(); + + // + return result; + } + + public static XMongoRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagMongoRepository, + IXTagSeeder, + TSeederImplementation + > GetXTagMongoRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XMongoRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagMongoRepository, + IXTagSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region InMemory Repository Descriptor Provider(s) ... + public static XInMemoryRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagInMemoryRepository + > GetXTagInMemoryRepositoryDescriptor() + { + // + var result = new XInMemoryRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagInMemoryRepository + >(); + + // + return result; + } + + public static XInMemoryRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagInMemoryRepository, + IXTagSeeder, + TSeederImplementation + > GetXTagInMemoryRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XInMemoryRepositoryDescriptor< + XTag, + int, + IXTagKeyGenerator, + XTagKeyGenerator, + IXTagRepositoryEvents, + XTagRepositoryEvents, + XTagGraphType, + IntGraphType, + XTagGraphQuery, + IXTagGraphQLTypeHelper, + XTagGraphQLTypeHelper, + XTagGraphSchema, + IXTagRepository, + XTagInMemoryRepository, + IXTagSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\Dtos\IXTagDtoHub.cs + +using xPushService.Interfaces; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; + +namespace xTagService.Interfaces.Dtos +{ + public interface IXTagDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\Dtos\IXTagRepositoryService.cs + +using xDataService.Interfaces; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; + +namespace xTagService.Interfaces.Dtos +{ + public interface IXTagRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\Dtos\IXTagServiceProvider.cs + +using xPushService.Interfaces; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; +using xTagService.Providers.Dtos; + +namespace xTagService.Interfaces.Dtos +{ + public interface IXTagServiceProvider : IXBaseDtoProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\Entities\IXTagEntityHub.cs + +using xPushService.Interfaces; +using xTagService.Models.Entities; + +namespace xTagService.Interfaces.Entities +{ + public interface IXTagEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\Entities\IXTagGraphQLTypeHelper.cs + +using xDataService.Interfaces; +using xTagService.Models.Entities; + +namespace xTagService.Interfaces.Entities +{ + public interface IXTagGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\Entities\IXTagKeyGenerator.cs + +using xDataService.Interfaces; +using xTagService.Models.Entities; + +namespace xTagService.Interfaces.Entities +{ + public interface IXTagKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\Entities\IXTagRepository.cs + +using xDataService.Interfaces; +using xTagService.Models.Entities; + +namespace xTagService.Interfaces.Entities +{ + public interface IXTagRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\Entities\IXTagRepositoryEvents.cs + +using xDataService.Interfaces; +using xTagService.Models.Entities; + +namespace xTagService.Interfaces.Entities +{ + public interface IXTagRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\Entities\IXTagRepositoryProvider.cs + +using xPushService.Interfaces; +using xTagService.Models.Entities; +using xTagService.Providers.Entities; + +namespace xTagService.Interfaces.Entities +{ + public interface IXTagRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\Entities\IXTagSeeder.cs + +using xDataService.Interfaces; +using xTagService.Models.Entities; + +namespace xTagService.Interfaces.Entities +{ + public interface IXTagSeeder : IXBaseDbSeeder + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\IXBaseProvidedHasTag.cs + +using xModels.Base; +using xPushService.Base; +using xPushService.Interfaces; + +namespace xTagService.Interfaces +{ + public interface IXBaseProvidedHasTag : IXBaseProvided, IXTagProvided + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\IXBaseProvidedHasTagController.cs + +using xModels.Base; +using xPushService.Base; +using xPushService.Interfaces; + +namespace xTagService.Interfaces +{ + public interface IXBaseProvidedHasTagController : IXBaseProvidedController, IXTagProvidedController + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\IXBaseTagProvider.cs + +using System.Threading; +using System.Threading.Tasks; +using xDataService.Interfaces; +using xDataService.Models; +using xTagService.Models.Dtos; + +namespace xTagService.Interfaces +{ + public interface IXBaseTagProvider : IXBaseReferencedProvider + { + // + #region Tools ... + /// + /// Add Reference a Tag to Specific Provided ID ... + /// + /// + /// + /// + /// + /// + Task AddReference( + string tag, + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Reference a Tag to Specific XRefence ... + /// + /// + /// + /// + /// + /// + Task AddReference( + string tag, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Reference a Tag for Specific Provided ID ... + /// + /// + /// + /// + /// + /// + Task RemoveReference( + string tag, + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Reference a Tag for Specific XRefence ... + /// + /// + /// + /// + /// + /// + Task RemoveReference( + string tag, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\IXTagProvided.cs + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xIdentityModels.Models; +using xModels.Base; +using xPushService.Base; + +namespace xTagService.Interfaces +{ + /// + /// a Tag Provided Service is a Model Provider Service which + /// can Contains Tags ... + /// + public interface IXTagProvided + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + /// + /// Attach Tag to Specified Item ... + /// + /// + /// + /// + /// + /// + /// + Task AttachTag( + TKey id, + string tag, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Detach Tag of Specified Item ... + /// + /// + /// + /// + /// + /// + /// + Task DetachTag( + TKey id, + string tag, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Attach Tags to Specified Item ... + /// + /// + /// + /// + /// + /// + /// + Task AttachTags( + TKey id, + IEnumerable tags, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Detach Tags of Specified Item ... + /// + /// + /// + /// + /// + /// + /// + Task DetachTags( + TKey id, + IEnumerable tags, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Detach all Attached Tags of Specified Item ... + /// + /// + /// + /// + /// + /// + Task DetachTags( + TKey id, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get Specified Item Tags ... + /// + /// + /// + /// + Task> GetTags( + TKey id, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\IXTagProvidedController.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using xModels.Base; +using xPushService.Base; + +namespace xTagService.Interfaces +{ + public interface IXTagProvidedController + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + /// + /// Attach Tag to Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/attach")] + Task AttachTag( + [FromRoute] TKey id, + [FromBody] XBaseValueContainer tag, + CancellationToken cancellationToken = default + ); + + /// + /// Detach Tag of Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/detach")] + Task DetachTag( + [FromRoute] TKey id, + [FromBody] XBaseValueContainer tag, + CancellationToken cancellationToken = default + ); + + /// + /// Attach Tags to Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/attachs")] + Task AttachTags( + [FromRoute] TKey id, + [FromBody] XBaseRangeRequest tags, + CancellationToken cancellationToken = default + ); + + /// + /// Detach Tags of Specified Item ... + /// + /// + /// + /// + /// + [HttpPost("{id}/tags/detachs")] + Task DetachTags( + [FromRoute] TKey id, + [FromBody] XBaseRangeRequest tags = null, + CancellationToken cancellationToken = default + ); + + /// + /// Get Specified Item Tags ... + /// + /// + /// + /// + [HttpGet("{id}/tags")] + Task>> GetTags( + [FromRoute] TKey id, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\IXTagProvider.cs + +using xPushService.Interfaces; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; +using xTagService.Providers.Dtos; + +namespace xTagService.Interfaces +{ + public interface IXTagProvider : IXBaseProvided + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Interfaces\IXTagProviderController.cs + +using xPushService.Interfaces; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; +using xTagService.Providers.Dtos; + +namespace xTagService.Interfaces +{ + public interface IXTagProviderController : IXBaseProvidedController + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Models\Dtos\XTagDto.cs + +using xModels.Base; + +namespace xTagService.Models.Dtos +{ + public class XTagDto : XBaseIntIDEntityDto + { + public string Tag { get; set; } + public string References { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Models\Entities\XTag.cs + +using System.ComponentModel.DataAnnotations; +using xModels.Base; + +namespace xTagService.Models.Entities +{ + /// + /// a Tag Model ... + /// + public class XTag : XBaseIntIDEntity + { + /// + /// Tag Label ... + /// + /// + [Required] + [StringLength(255)] + public string Tag { 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\xTagService\Providers\Dtos\XTagDtoHub.cs + +using Microsoft.Extensions.Logging; +using xPushService.Base; +using xTagService.Interfaces.Dtos; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Dtos +{ + public class XTagDtoHub : XBaseDtoHub, IXTagDtoHub + { + public XTagDtoHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\Dtos\XTagRepositoryService.cs + +using System.Collections.Generic; +using AutoMapper; +using xDataService.Providers; +using xTagService.Interfaces.Dtos; +using xTagService.Interfaces.Entities; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Dtos +{ + public class XTagRepositoryService : XBaseRepositoryService, IXTagRepositoryService + { + public XTagRepositoryService( + IXTagRepository repository, + IEnumerable mapperProfiles = null + ) : base( + repository, + mapperProfiles + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\Dtos\XTagServiceProvider.cs + +using Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; +using xTagService.Interfaces.Dtos; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Dtos +{ + public class XTagServiceProvider : XBaseDtoProvider, IXTagServiceProvider + { + public XTagServiceProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXTagRepositoryService service, + IXIdentityProvider identityProvider = null + ) : base( + hub, + dataConfiguration, + service, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\Entities\XTagEFRepository.cs + +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Providers; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Entities +{ + public class XTagEFRepository : XBaseEFRepository, IXTagRepository + where TContext : XDbContext + { + public XTagEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXTagKeyGenerator keyGenerator = null, + IXTagRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\Entities\XTagEntityHub.cs + +using Microsoft.Extensions.Logging; +using xPushService.Base; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Entities +{ + public class XTagEntityHub : XBaseEntityHub, IXTagEntityHub + { + public XTagEntityHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\Entities\XTagInMemoryRepository.cs + +using xDataService.Configuration; +using xDataService.Providers; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Entities +{ + public class XTagInMemoryRepository : XBaseInMemoryRepository, IXTagRepository + { + public XTagInMemoryRepository( + XDataServiceConfiguration configuration, + IXTagKeyGenerator keyGenerator = null, + IXTagRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\Entities\XTagKeyGenerator.cs + +using xDataService.Providers; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Entities +{ + public class XTagKeyGenerator : XIntKeyGenerator, IXTagKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\Entities\XTagMongoRepository.cs + +using xDataService.Configuration; +using xDataService.Providers; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Entities +{ + public class XTagMongoRepository : XBaseMongoRepository, IXTagRepository + { + public XTagMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXTagKeyGenerator keyGenerator = null, + IXTagRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\Entities\XTagRepositoryEvents.cs + +using xDataService.Providers; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Entities +{ + public class XTagRepositoryEvents : XBaseRepositoryEvents, IXTagRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\Entities\XTagRepositoryProvider.cs + +using Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Entities +{ + public class XTagRepositoryProvider : XBaseEntityProvider, IXTagRepositoryProvider + { + public XTagRepositoryProvider( + IHubContext hub, + IXTagRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + repository, + dataConfiguration, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\Entities\XTagSeederBase.cs + +using xDataService.Configuration; +using xDataService.Providers; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers.Entities +{ + public abstract class XTagSeederBase : XBaseDbSeeder, IXTagSeeder + { + protected XTagSeederBase( + string entity, + string database, + IXTagRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + entity, + database, + repository, + dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\GraphQL\XTagGraphQLTypeHelper.cs + +using xDataService.Configuration; +using xDataService.GraphQL; +using xTagService.Constants; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers.GraphQL +{ + public class XTagGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXTagGraphQLTypeHelper + { + public XTagGraphQLTypeHelper( + XDataServiceConfiguration configuration + ) : base(configuration) + { } + + public override string GetInQueryCollectionName() + { + return XTagServiceConstants.XTagCollectionName; + } + + public override string GetInQuerySingleName() + { + return XTagServiceConstants.XTagSingleName; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\GraphQL\XTagGraphQuery.cs + +using GraphQL.Types; +using xDataService.Configuration; +using xDataService.GraphQL; +using xTagService.Interfaces.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers.GraphQL +{ + public class XTagGraphQuery : XBaseGraphQLQuery + { + public XTagGraphQuery( + XDataServiceConfiguration configuration, + IXTagRepository repository, + IXTagGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\GraphQL\XTagGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xTagService.Providers.GraphQL +{ + public class XTagGraphSchema : Schema + { + public XTagGraphSchema(IServiceProvider services) : base(services) + { + Query = (XTagGraphQuery)services.GetService(typeof(XTagGraphQuery)); + } + + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\GraphQL\XTagGraphType.cs + +using xDataService.GraphQL; +using xTagService.Models.Entities; + +namespace xTagService.Providers.GraphQL +{ + public class XTagGraphType : XBaseGraphObjectType + { + public XTagGraphType() : base() + { + Field(x => x.Tag); + Field(x => x.References); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\XBaseProvidedHasTag.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using xCommons.Extensions; +using xIdentityModels.Models; +using xModels.Base; +using xPushService.Base; +using xPushService.Interfaces; +using xTagService.Interfaces; +using xIdentityModels.Extensions; +using System.Threading; +using xExceptions.Constants; +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore.Query; +using xModels.Dtos; + +namespace xTagService.Providers +{ + public abstract class XBaseProvidedHasTag : IXBaseProvidedHasTag + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + private readonly string[] permittedRoles; + private readonly IXBaseTagProvider tagProvider; + private readonly IXBaseDtoProvider provider; + + protected XBaseProvidedHasTag( + IXBaseTagProvider tagProvider, + IXBaseDtoProvider provider, + string[] permittedRoles = null + ) + { + this.provider = provider; + this.tagProvider = tagProvider; + this.permittedRoles = permittedRoles; + } + + /// + /// Check an Items is Owned for User or not ... + /// + /// + /// + /// + public abstract bool IsOwned( + TDto item, + XUserClaimsInfoDto userInfo + ); + public abstract bool IsOwned( + TEntity item, + XUserClaimsInfoDto userInfo + ); + + /// + /// Check User Has Permission for Manipulate Data or not ... + /// + /// + /// + public bool HasPermision( + XUserClaimsInfoDto userInfo + ) + { + // + var result = + !userInfo.IsNullOrDefault() && + userInfo.HasAccess(permittedRoles); + + // + return result; + } + + // + #region Tag ... + /// + /// Attach Tag to Specified Item ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task AttachTag( + TKey id, + string tag, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull() && + !tag.IsNullOrEmpty() && + !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var item = await provider.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + isValid = + !item.IsNullOrDefault() && + (IsOwned(item, userInfo) || + HasPermision(userInfo)); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + await tagProvider.AddReference( + tag: tag, + providedForId: id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + catch { } + } + + /// + /// Detach Tag fro Specified Item ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task DetachTag( + TKey id, + string tag, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull() && + !tag.IsNullOrEmpty() && + !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var item = await provider.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + isValid = + !item.IsNullOrDefault() && + (IsOwned(item, userInfo) || + HasPermision(userInfo)); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + await tagProvider.RemoveReference( + tag: tag, + providedForId: id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + catch { } + } + + /// + /// Attach Tags for Specified Item ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task AttachTags( + TKey id, + IEnumerable tags, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull() && + !tags.IsNull() && + tags.HasChild(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Loop through Tags ... + foreach (var tag in tags) + { + // + // Check Cancellation Token ... + if (cancellationToken.IsCancellationRequested) + { + break; + } + + // + await AttachTag( + id: id, + tag: tag, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + } + + /// + /// Detach Tags for Specified Item ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task DetachTags( + TKey id, + IEnumerable tags, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull() && + !tags.IsNull() && + tags.HasChild(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Loop through Tags ... + foreach (var tag in tags) + { + // + // Check Cancellation Token ... + if (cancellationToken.IsCancellationRequested) + { + break; + } + + // + await DetachTag( + id: id, + tag: tag, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + } + + /// + /// Detach all Attached Tags for Specified Item ... + /// + /// + /// + /// + /// + /// + public virtual async Task DetachTags( + TKey id, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var item = await provider.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + isValid = + !item.IsNullOrDefault() && + (IsOwned(item, userInfo) || + HasPermision(userInfo)); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + await tagProvider.RemoveReferences( + providedForId: id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + catch { } + } + + /// + /// Get Specified Model's Item ... + /// + /// + /// + /// + public virtual async Task> GetTags( + TKey id, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = !id.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + isValid = await provider.IsExistsAsync( + id: id, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + var result = (await tagProvider.GetAllReferences( + providedForId: id, + cancellationToken: cancellationToken + )) + .Select(x => x.Tag) + .Distinct() + .ToList(); + + // + return result; + } + #endregion + + // + #region Provided ... + /// + /// Get Specified Item by ID ... + /// + /// + /// + /// + /// + public virtual async Task Get( + TKey id, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Get All Exists Items ... + /// + /// + /// + /// + /// + public virtual async Task> GetAll( + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.GetAllAsync( + ignoreSoftDeleteds: true, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// find an item by providing a Conditional Expression ... + /// + /// + /// + /// + /// + public virtual async Task FindOne( + Expression> predicate, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.FindOneAsync( + predicate: predicate, + ignoreSoftDeleteds: true, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// find a collection of Items by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public virtual async Task> FindMany( + Expression> predicate, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.FindManyAsync( + predicate: predicate, + ignoreSoftDeleteds: true, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// retrieve Items based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task> Query( + XQuery query, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.QueryAsync( + query: query, + predicate: predicate, + ignoreSoftDeleteds: true, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// retrieve Owned Items based on XQuery Pagination structure ... + /// if supported ... + /// + /// + /// + /// + /// + /// + /// + /// + public virtual async Task> QueryOwned( + XQuery query, + XUserClaimsInfoDto userInfo = null, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + Expression> predicateor = null; + if (!predicate.IsNull()) + { + // + var predicateFnc = predicate.Compile(); + predicateor = x => + predicateFnc(x) && + IsOwned(x, userInfo); + } + else + { + predicateor = x => IsOwned(x, userInfo); + } + + // + var result = await provider.QueryAsync( + query: query, + predicate: predicateor, + ignoreSoftDeleteds: true, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Add an item values ... + /// + /// + /// + /// + /// + /// + public virtual async Task Add( + TDto item, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.AddAsync( + item: item, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Update an item values ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task Update( + TKey id, + TDto item, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Check Permission ... + var hasPermission = + IsOwned(item, userInfo) || + HasPermision(userInfo); + if (!hasPermission) + { + XException.NotAllowed.Throw(); + } + + // + var result = await provider.UpdateAsync( + id: id, + item: item, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// remove an Item ... + /// + /// + /// + /// + /// + /// + public virtual async Task Remove( + TKey id, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Check Permission ... + var item = await Get( + id: id, + cancellationToken: cancellationToken + ); + var hasPermission = + IsOwned(item, userInfo) || + HasPermision(userInfo); + if (!hasPermission) + { + XException.NotAllowed.Throw(); + } + + // + var result = await provider.RemoveAsync( + id: id, + saveChanges: true, + softDelete: false, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// count all exists Items ... + /// + /// + /// + public virtual async Task Count( + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.CountAsync( + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Check an Item exists or not ... + /// + /// + /// + /// + public virtual async Task IsExists( + TKey id, + CancellationToken cancellationToken = default + ) + { + // + var result = await provider.IsExistsAsync( + id: id, + cancellationToken: cancellationToken + ); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\XBaseTagProvided.cs + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xIdentityModels.Models; +using xModels.Base; +using xPushService.Base; +using xPushService.Interfaces; +using xTagService.Interfaces; +using xIdentityModels.Extensions; +using xExceptions.Constants; +using System.Linq; + +namespace xTagService.Providers +{ + public abstract class XBaseTagProvided : IXTagProvided + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + where THub : XBaseDtoHub + { + private readonly string[] permittedRoles; + private readonly IXBaseTagProvider tagProvider; + private readonly IXBaseDtoProvider provider; + + protected XBaseTagProvided( + IXBaseTagProvider tagProvider, + IXBaseDtoProvider provider, + string[] permittedRoles = null + ) + { + this.provider = provider; + this.tagProvider = tagProvider; + this.permittedRoles = permittedRoles; + } + + /// + /// Check an Items is Owned for User or not ... + /// + /// + /// + /// + public abstract bool IsOwned( + TDto item, + XUserClaimsInfoDto userInfo + ); + public abstract bool IsOwned( + TEntity item, + XUserClaimsInfoDto userInfo + ); + + /// + /// Check User Has Permission for Manipulate Data or not ... + /// + /// + /// + public bool HasPermision( + XUserClaimsInfoDto userInfo + ) + { + // + var result = + !userInfo.IsNullOrDefault() && + userInfo.HasAccess(permittedRoles); + + // + return result; + } + + // + #region Actions ... + /// + /// Attach Tag to Specified Item ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task AttachTag( + TKey id, + string tag, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull() && + !tag.IsNullOrEmpty() && + !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var item = await provider.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + isValid = + !item.IsNullOrDefault() && + (IsOwned(item, userInfo) || + HasPermision(userInfo)); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + await tagProvider.AddReference( + tag: tag, + providedForId: id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + catch { } + } + + /// + /// Detach Tag fro Specified Item ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task DetachTag( + TKey id, + string tag, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull() && + !tag.IsNullOrEmpty() && + !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var item = await provider.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + isValid = + !item.IsNullOrDefault() && + (IsOwned(item, userInfo) || + HasPermision(userInfo)); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + await tagProvider.RemoveReference( + tag: tag, + providedForId: id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + catch { } + } + + /// + /// Attach Tags for Specified Item ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task AttachTags( + TKey id, + IEnumerable tags, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull() && + !tags.IsNull() && + tags.HasChild(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Loop through Tags ... + foreach (var tag in tags) + { + // + // Check Cancellation Token ... + if (cancellationToken.IsCancellationRequested) + { + break; + } + + // + await AttachTag( + id: id, + tag: tag, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + } + + /// + /// Detach Tags for Specified Item ... + /// + /// + /// + /// + /// + /// + /// + public virtual async Task DetachTags( + TKey id, + IEnumerable tags, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull() && + !tags.IsNull() && + tags.HasChild(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Loop through Tags ... + foreach (var tag in tags) + { + // + // Check Cancellation Token ... + if (cancellationToken.IsCancellationRequested) + { + break; + } + + // + await DetachTag( + id: id, + tag: tag, + userInfo: userInfo, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + } + + /// + /// Detach all Attached Tags for Specified Item ... + /// + /// + /// + /// + /// + /// + public virtual async Task DetachTags( + TKey id, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !id.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var item = await provider.GetAsync( + id: id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + isValid = + !item.IsNullOrDefault() && + (IsOwned(item, userInfo) || + HasPermision(userInfo)); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + await tagProvider.RemoveReferences( + providedForId: id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + catch { } + } + + /// + /// Get Specified Model's Item ... + /// + /// + /// + /// + public virtual async Task> GetTags( + TKey id, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = !id.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + isValid = await provider.IsExistsAsync( + id: id, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + var result = (await tagProvider.GetAllReferences( + providedForId: id, + cancellationToken: cancellationToken + )) + .Select(x => x.Tag) + .Distinct() + .ToList(); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\XBaseTagProvider.cs + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xDataService.Extensions; +using xDataService.Models; +using xExceptions.Constants; +using xModels.Dtos; +using xTagService.Extensions; +using xTagService.Interfaces; +using xTagService.Interfaces.Dtos; +using xTagService.Models.Dtos; + +namespace xTagService.Providers +{ + public abstract class XBaseTagProvider : IXBaseTagProvider + { + // + #region Props ... + /// + /// Provider Identifier ... + /// + public string Provider { get; } + + /// + /// Tag Provider for Maipulating Tags ... + /// + public IXTagServiceProvider TagProvider { get; } + #endregion + + // + #region Constructor ... + public XBaseTagProvider( + string providedFor, + IXTagServiceProvider tagProvider + ) + { + // + Provider = providedFor; + TagProvider = tagProvider; + } + #endregion + + // + #region Referenced Actions ... + /// + /// Get Specified Indexed Reference for Specific Provided ID ... + /// + /// + /// + /// + /// + public async Task GetReference( + TKey providedForId, + int forIndex = 0, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + if (forIndex < 0) + { + forIndex = 0; + } + + // + // Retrieve Last Index ... + var maxIndex = (await GetReferencingIndexes( + providedForId: providedForId, + cancellationToken: cancellationToken + )) + .Max(); + if (forIndex > maxIndex) + { + XException.NotFound.Throw(); + } + + // + var indexedIdentifier = GetIndexedProvidedID( + forIndex: forIndex, + providedForId: providedForId + ); + var result = await TagProvider.FindOneAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: x => x.References.Contains(indexedIdentifier) + ); + if (result.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + return result; + } + + /// + /// Retrieve all Exists References of Specific Provided ID ... + /// + /// + /// + /// + public async Task> GetAllReferences( + TKey providedForId, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + var identifier = GetProvidedID(providedForId); + var result = await TagProvider.FindManyAsync( + orderBuilder: null, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: x => x.References.Contains(identifier) + ); + + // + return result; + } + + /// + /// Extract all Referencing Models for Specified Key ... + /// + /// + /// + /// + public async Task>> GetAllReferencings( + TKey providedForId, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !providedForId.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var identifier = GetProvidedID(providedForId); + + // + var result = (await TagProvider.FindManyAsync( + orderBuilder: null, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: x => x.References.Contains(identifier) + )) + .Select(x => x.References) + .ToList() + .SelectMany(x => x.ParseXReferenceList()) + .Where(x => x.ProvidedFor == Provider && $"{x.ReferencedTo}" == $"{providedForId}") + .OrderBy(x => x.Index) + .AsEnumerable(); + + // + return result; + } + + /// + /// Count References ... + /// + /// + /// + /// + public async Task CountReferences( + TKey providedForId, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = !providedForId.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var identifier = GetProvidedID(providedForId); + var result = (await TagProvider.FindManyAsync( + orderBuilder: null, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: x => x.References.Contains(identifier) + )) + .Count(); + + // + return result; + } + + /// + /// Retrieve References of Specific Provided ID as Query ... + /// + /// + /// + /// + public async Task> QueryReferences( + XQuery query, + TKey providedForId, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (query.IsNull() || providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + var identifier = GetProvidedID(providedForId); + var result = await TagProvider.QueryAsync( + query: query, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + orderBuilder: x => x.OrderBy(y => y.Id), + predicate: x => x.References.Contains(identifier) + ); + + // + return result; + } + + /// + /// Add Reference a Model to Specific Provided ID ... + /// + /// + /// + /// + /// + /// + public async Task AddReference( + XTagDto model, + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = false; + + // + // Validate ... + result = !model.IsNullOrDefault() && + !model.Tag.IsNullOrEmpty() && + model.Id.IsValidIntId() && + !providedForId.IsNull(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Model Exists ... + result = await TagProvider.IsExistsAsync(model.Id); + if (!result) + { + XException.NotFound.Throw(); + } + + // + // Update Model by Retrieving ... + model = await TagProvider.GetAsync( + id: model.Id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + + // + // Retrieve all References ... + var references = await GetAllReferences( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + // Check isReferenced or not ... + result = references + .Any(r => + r.Id == model.Id && + r.GetReferences() + .Any(rf => + rf.ProvidedFor == Provider && + rf.ReferencedTo == $"{providedForId}")); + if (result) + { + return result; + } + + // + // Here means there is not any reference to ProvidedForId ... + int forIndex = await CountReferences( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + var modelReferences = model.GetReferences(); + modelReferences.Add(new XReference + { + Index = forIndex, + ProvidedFor = Provider, + ReferencedTo = $"{providedForId}" + }); + modelReferences = modelReferences + .OrderBy(r => r.Index) + .ToList(); + model = model.UpdateReferences(modelReferences); + + // + // Update Data Base ... + model = await TagProvider.UpdateAsync( + id: model.Id, + item: model, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + result = !model.IsNullOrDefault(); + + // + return result; + } + + /// + /// Add Reference a Model to Specific XRefence ... + /// + /// + /// + /// + /// + /// + public async Task AddReference( + XTagDto model, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + // Since other Validations Handled in Calling, we Ignore them here ... + var result = + !model.IsNull() && + !reference.IsNullOrDefault() && + reference.ProvidedFor == Provider && + !reference.ReferencedTo.IsNull(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await AddReference( + model: model, + connectionId: connectionId, + cancellationToken: cancellationToken, + providedForId: reference.ReferencedTo + ); + + // + return result; + } + + /// + /// Remove Reference a Model for Specific Provided ID ... + /// + /// + /// + /// + /// + /// + public async Task RemoveReference( + XTagDto model, + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = + !providedForId.IsNull() && + !model.IsNullOrDefault() && + model.Id.IsValidIntId(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Model ... + model = await TagProvider.GetAsync( + id: model.Id, + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + result = !model.IsNullOrDefault(); + if (!result) + { + XException.NotFound.Throw(); + } + + // + // Reading Referencing Indexes ... + var indexes = await GetReferencingIndexes( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + // Check Model Reference to ID ... + var referencesCount = await CountReferences( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + 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 + .GetReferences() + .FirstOrDefault(r => + r.ProvidedFor == Provider && + r.ReferencedTo == $"{providedForId}"); + result = !reference.IsNullOrDefault(); + if (!result) + { + XException.ActionFailed.Throw(); + } + + // + // Remove Referenced Item from Model ... + var modelReferences = model + .GetReferences() + .Where(r => + !(r.ProvidedFor == Provider && + r.ReferencedTo == $"{providedForId}")) + .ToList(); + model = model.UpdateReferences(modelReferences); + + // + // Update DataBase ... + model = await TagProvider.UpdateAsync( + item: model, + id: model.Id, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + + // + 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, + cancellationToken: cancellationToken + ); + if (!dto.IsNullOrDefault()) + { + // + var dtoReferences = dto.GetReferences(); + dtoReferences + .ToList() + .ForEach(iref => + { + // + if ( + iref.Index == i && + iref.ProvidedFor == Provider && + iref.ReferencedTo == reference.ReferencedTo + ) + { + iref.Index = i - 1; + } + }); + dto = dto.UpdateReferences(dtoReferences); + + // + dto = await TagProvider.UpdateAsync( + item: dto, + id: dto.Id, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + } + } + } + + // + return result; + } + + /// + /// Remove Reference a Model for Specific XRefence ... + /// + /// + /// + /// + /// + /// + public async Task RemoveReference( + XTagDto model, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + if (model.IsNullOrDefault() || + reference.IsNullOrDefault() || + reference.ReferencedTo.IsNull() || + reference.ProvidedFor != Provider) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await RemoveReference( + model: model, + connectionId: connectionId, + cancellationToken: cancellationToken, + providedForId: reference.ReferencedTo + ); + + // + return result; + } + + /// + /// Remove All References to Specified Key ... + /// + /// + /// + /// + /// + public async Task RemoveReferences( + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = false; + var references = await GetAllReferences( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + if (!references.IsNull() && references.HasChild()) + { + // + foreach (var reference in references) + { + // + // Remove Reference ... + var itemReferences = reference.GetReferences() + .Where(r => + r.ProvidedFor != Provider || + (r.ProvidedFor == Provider && + r.ReferencedTo != $"{providedForId}")) + .ToList(); + var updatedModel = reference.UpdateReferences(itemReferences); + updatedModel = await TagProvider.UpdateAsync( + saveChanges: true, + item: updatedModel, + id: updatedModel.Id, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + if (!updatedModel.IsNullOrDefault()) + { + result = true; + } + } + } + + // + return result; + } + #endregion + + // + #region Tools ... + /// + /// Add Reference a Tag to Specific Provided ID ... + /// + /// + /// + /// + /// + /// + public async Task AddReference( + string tag, + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = false; + + // + // Validate ... + result = !tag.IsNullOrEmpty(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Tag Exists ... + result = await IsExists( + tag: tag, + cancellationToken: cancellationToken + ); + if (!result) + { + // + // Create Tag if Not Exists ... + var tagDto = new XTagDto + { + Tag = tag, + }; + tagDto = await TagProvider.AddAsync( + item: tagDto, + saveChanges: true, + connectionId: connectionId, + cancellationToken: cancellationToken + ); + result = !tagDto.IsNullOrDefault(); + if (!result) + { + XException.ActionFailed.Throw(); + } + } + + // + var dto = await GetTagByTag( + tag: tag, + cancellationToken: cancellationToken + ); + result = !dto.IsNullOrDefault(); + if (!result) + { + XException.ActionFailed.Throw(); + } + + // + result = await AddReference( + model: dto, + connectionId: connectionId, + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Add Reference a Tag to Specific XRefence ... + /// + /// + /// + /// + /// + /// + public async Task AddReference( + string tag, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var result = false; + + // + // Validate ... + result = + !tag.IsNullOrEmpty() && + !reference.IsNullOrDefault() && + reference.ProvidedFor == Provider && + !reference.ReferencedTo.IsNull(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await AddReference( + tag: tag, + connectionId: connectionId, + cancellationToken: cancellationToken, + providedForId: reference.ReferencedTo + ); + + // + return result; + } + + /// + /// Remove Reference a Tag for Specific Provided ID ... + /// + /// + /// + /// + /// + /// + public async Task RemoveReference( + string tag, + TKey providedForId, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = + !tag.IsNullOrEmpty() && + !providedForId.IsNull(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Tag Exists ... + result = await IsExists( + tag: tag, + cancellationToken: cancellationToken + ); + if (!result) + { + XException.NotFound.Throw(); + } + + // + var dto = await GetTagByTag( + tag: tag, + cancellationToken: cancellationToken + ); + result = !dto.IsNullOrDefault(); + if (!result) + { + XException.ActionFailed.Throw(); + } + + // + result = await RemoveReference( + model: dto, + connectionId: connectionId, + providedForId: providedForId, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Remove Reference a Tag for Specific XRefence ... + /// + /// + /// + /// + /// + /// + public async Task RemoveReference( + string tag, + XReference reference, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = + !tag.IsNullOrEmpty() && + !reference.IsNullOrDefault() && + !reference.ReferencedTo.IsNull() && + reference.ProvidedFor == Provider; + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await RemoveReference( + tag: tag, + connectionId: connectionId, + cancellationToken: cancellationToken, + providedForId: reference.ReferencedTo + ); + + // + 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, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !providedForId.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var result = new List(); + var referencings = await GetAllReferencings( + providedForId: providedForId, + cancellationToken: cancellationToken + ); + isValid = + !referencings.IsNull() && + referencings.HasChild(); + if (!isValid) + { + return result; + } + + // + result = + referencings + .OrderBy(r => r.Index) + .Select(r => r.Index) + .ToList(); + + // + return result; + } + + private async Task GetTagByTag( + string tag, + CancellationToken cancellationToken = default + ) + { + // + XTagDto result = null; + + // + if (!tag.IsNullOrEmpty()) + { + // + result = await TagProvider + .FindOneAsync( + predicate: x => x.Tag == tag, + cancellationToken: cancellationToken + ); + } + + // + return result; + } + + private async Task IsExists( + string tag, + CancellationToken cancellationToken = default + ) + { + // + var result = false; + + // + if (!tag.IsNullOrEmpty()) + { + // + var dto = await GetTagByTag( + tag: tag, + cancellationToken: cancellationToken + ); + result = !dto.IsNullOrDefault(); + } + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\XTagEntityRegisterar.cs + +using Microsoft.EntityFrameworkCore; +using xDataService.Interfaces; +using xDataService.Providers; +using xTagService.Configurations.Entities; +using xTagService.Models.Entities; + +namespace xTagService.Providers +{ + public class XTagEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar + { + public XTagEntityRegisterar() + : base(nameof(XTagEntityRegisterar)) + { + // + // Add XTag Entity ... + AddEntity(); + } + + /// + /// Configure Entities ... + /// + /// + public override void ConfigureEntities(ModelBuilder modelBuilder) + { + // + // Configure Entity Using Provided ... + modelBuilder + .ApplyConfigurationsFromAssembly(typeof(XTagEntityConfiguration).Assembly); + + // + base.ConfigureEntities(modelBuilder); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTagService\Providers\XTagProvider.cs + +using xCommons.Extensions; +using xIdentityModels.Models; +using xPushService.Providers; +using xTagService.Interfaces; +using xTagService.Interfaces.Dtos; +using xTagService.Models.Dtos; +using xTagService.Models.Entities; +using xTagService.Providers.Dtos; + +namespace xTagService.Providers +{ + public class XTagProvider : XBaseProvided, IXTagProvider + { + public XTagProvider( + IXTagServiceProvider provider + ) : base( + provider, + permittedRoles: new string[] { } + ) + { } + + public override bool IsOwned( + XTagDto item, + XUserClaimsInfoDto userInfo + ) + { + return !item.IsNullOrDefault(); + } + + public override bool IsOwned( + XTag item, + XUserClaimsInfoDto userInfo + ) + { + return !item.IsNullOrDefault(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Configurations\XTermsAndConditionsServiceConfiguration.cs + +using System.Collections.Generic; +using xDataService.Constants; +using xStringService.Models.Dtos; + +namespace xTermsAndConditionsService.Configurations +{ + /// + /// Terms and Conditions Configuration ... + /// + public class XTermsAndConditionsServiceConfiguration + { + /// + /// Terms And Conditions Resource Title in XString Table ... + /// + public string ResourceTitle { get; set; } = "terms_and_conditions"; + + /// + /// Seeding Mode for Terms and Conditions ... + /// + public XDbSeedingMode SeedingMode { get; set; } = XDbSeedingMode.AddIfNotExists; + + /// + /// Default Terms and Conditions for Add to Databse on App Startup ... + /// + public List TermsAndConditions { get; set; } = null; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Constants\ConfigurationNodeNames.cs + +namespace xTermsAndConditionsService.Constants +{ + public partial struct ConfigurationNodeNames + { + public const string TERMS_AND_CONDITIONS_SERVICE_NODE = "TermsAndConditionsService"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Constants\XTermsAndConditionsServiceConstants.cs + +namespace xTermsAndConditionsService.Constants +{ + public struct XTermsAndConditionsServiceConstants + { + // + // Service Extensions Log Tag ... + public const string XTermsAndConditionsServiceDILogTag = "XTermsAndConditionsService"; + + // + // Table Mapping Identifiers ... + + // + // GraphQL Collection Mappings ... + + // + // Hubs ... + + // + // Custome Constants ... + + // + // EF Repositories Helper Extensions ... + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Controllers\XTermsAndConditionsControllerBase.cs + +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Interfaces; +using xStringService.Controllers; +using xTermsAndConditionsService.Interfaces; + +namespace xTermsAndConditionsService.Controllers +{ + /// + /// a Base Controller Implementation for Providing Base Actions of + /// Terms and Conditions Provided Actions ... + /// + [AllowAnonymous] + public abstract class XTermsAndConditionsControllerBase : XBaseResourceProviderControllerBase, IXTermsAndConditionsController + { + protected XTermsAndConditionsControllerBase( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider, + IXTermsAndConditionsProvider provider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider, + provider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\DI\XDIHelperExtension.cs + +using System; +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Configurations; +using xCommons.Extensions; +using xExceptions.Constants; +using xPushService.DI; +using xPushService.Helpers; +using xStringService.Interfaces.Dtos; +using xTermsAndConditionsService.Configurations; +using xTermsAndConditionsService.Constants; +using xTermsAndConditionsService.Hubs; +using xTermsAndConditionsService.Interfaces; +using xTermsAndConditionsService.Providers; + +namespace xTermsAndConditionsService.DI +{ + public static class XDIHelperExtension + { + /// + /// Retrieve xTermsAndConditionsService Configuration from AppSettings ... + /// + /// + /// + public static XTermsAndConditionsServiceConfiguration GetXTermsAndConditionsServiceConfiguration( + this IConfiguration configuration + ) + { + // + var section = configuration.GetSection(ConfigurationNodeNames.TERMS_AND_CONDITIONS_SERVICE_NODE); + var result = section.Get(); + + // + return result; + } + + /// + /// Extract xTermsAndConditionsService Configurations from AppSettings + /// and Register it in DI ... + /// + /// + /// + public static void AddXTermsAndConditionsConfiguration( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + var serviceConfiguration = configuration.GetXTermsAndConditionsServiceConfiguration(); + services.AddXTermsAndConditionsConfiguration(serviceConfiguration); + } + + /// + /// Register xTermsAndConditionsService Configurations in DI ... + /// + /// + /// + public static void AddXTermsAndConditionsConfiguration( + this IServiceCollection services, + XTermsAndConditionsServiceConfiguration configuration + ) + { + // + if (configuration.IsNull()) + { + Log($"there is no provided configurations, using default ..."); + configuration = new XTermsAndConditionsServiceConfiguration(); + } + + // + services.AddSingleton(configuration); + } + + /// + /// Register TermsConditions Provider ... + /// + /// + /// provided AppSettings Configuration + /// + public static void AddXTermsConditionsService( + this IServiceCollection services, + IConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Transient + ) + { + // + AddTermsAndConditionsService( + services: services, + lifeTime: lifeTime, + configuration: configuration + ); + } + + /// + /// Register TermsConditions Provider ... + /// + /// + /// provided xTermsAndConditionsService Configuration + /// + public static void AddXTermsConditionsService( + this IServiceCollection services, + XTermsAndConditionsServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Transient + ) + { + // + AddTermsAndConditionsService( + services: services, + lifeTime: lifeTime, + serviceConfiguration: configuration + ); + } + + public static void UseXTermsAndConditionsService( + this IApplicationBuilder app + ) + { + // + // Create an Scope for Services Acess ... + using (var scope = app.ApplicationServices.CreateScope()) + { + // + // Inject Requirements ... + var stringProvider = scope.ServiceProvider.GetService(); + var serviceConfiguration = scope.ServiceProvider.GetService(); + + // + // Using XTerms Hub ... + var pushHelper = new XPushServiceHelper(); + pushHelper.AddHub("terms"); + app.UseXPushService(pushHelper); + + // + // Validate Requirements Exists ... + var isValid = + !stringProvider.IsNull() && + !serviceConfiguration.IsNull() && + !serviceConfiguration.TermsAndConditions.IsNull() && + serviceConfiguration.TermsAndConditions.HasChild() && + serviceConfiguration.SeedingMode != xDataService.Constants.XDbSeedingMode.None; + if (isValid) + { + // + // Prepare Default Terms ... + serviceConfiguration + .TermsAndConditions + .ToList() + .ForEach(t => t.ResourceTitle = serviceConfiguration.ResourceTitle); + + // + // Loop through Terms and Conditions for Seeding ... + foreach (var item in serviceConfiguration.TermsAndConditions) + { + // + // Detect Exists Item ... + var exists = stringProvider.FindOneAsync( + predicate: x => + x.Language == item.Language && + x.ResourceTitle == item.ResourceTitle + ) + .GetAwaiter() + .GetResult(); + + // + var isExists = !exists.IsNullOrDefault(); + + // + switch (serviceConfiguration.SeedingMode) + { + // + case xDataService.Constants.XDbSeedingMode.AddOrUpdate: + // + // Update Exists by new Provided ... + exists.TranslatedValue = item.TranslatedValue; + exists = stringProvider.UpdateAsync( + id: exists.Id, + item: exists, + saveChanges: true + ) + .GetAwaiter() + .GetResult(); + + // + Log($"Refresh Terms and Conditions of {exists.Language}"); + break; + + // + case xDataService.Constants.XDbSeedingMode.AddIfNotExists: + // + if (!isExists) + { + // + exists = stringProvider.AddAsync( + item: item, + saveChanges: true + ) + .GetAwaiter() + .GetResult(); + + // + Log($"Add Terms and Conditions for {exists.Language}"); + } + break; + } + } + } + } + } + + // + #region Private ... + /// + /// a LogTag for Service ... + /// + private static string XLogTag = XTermsAndConditionsServiceConstants.XTermsAndConditionsServiceDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Add Terms and Conditions Provider Service ... + /// + /// + /// + private static void AddTermsAndConditionsService( + IServiceCollection services, + IConfiguration configuration = null, + XTermsAndConditionsServiceConfiguration serviceConfiguration = null, + ServiceLifetime lifeTime = ServiceLifetime.Transient + ) + { + // + var exception = XException.InvalidArgs.ToException(); ; + + // + // Services ... + var isServicesExists = !services.IsNull(); + if (!isServicesExists) + { + // + Log("AddTermsAndConditions failed, services not provided ..."); + throw exception; + } + + // + // Validate Configuration ... + var isConfigValid = !configuration.IsNull() || !serviceConfiguration.IsNull(); + if (!isConfigValid) + { + // + Log("AddTermsAndConditions failed, configuration not provided ..."); + throw exception; + } + + // + // Lifetime ... + var isLifetimeExists = !lifeTime.IsNull(); + if (!isLifetimeExists) + { + // + Log("AddTermsAndConditions failed, lifetime not provided ..."); + throw exception; + } + + // + // Check Dependencies Exists ... + var isDependenciesPassed = !services + .GetRegisteredService() + .IsNull() && + !services.GetRegisteredService() + .IsNull(); + if (!isDependenciesPassed) + { + // + Log("AddTermsAndConditions failed due Dependencies Issues, IXStringServiceProvider or XAppConfiguration not provided ..."); + throw exception; + } + + // + // Service Configuration Handler ... + if (serviceConfiguration.IsNull()) + { + serviceConfiguration = configuration.GetXTermsAndConditionsServiceConfiguration(); + } + services.AddXTermsAndConditionsConfiguration(serviceConfiguration); + + // + // Register Resource Providers ... + services.Add(new ServiceDescriptor(typeof(IXTermsAndConditionsProvider), typeof(XTermsAndConditionsProvider), lifeTime)); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Hubs\XTermsHub.cs + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xPushService.Base; +using xPushService.Constants; +using xStringService.Models.Dtos; + +namespace xTermsAndConditionsService.Hubs +{ + /// + /// a Hub for Notifing Terms and Conditions Events to Connected Clients ... + /// + public class XTermsHub : XBaseHub + { + public XTermsHub( + ILogger logger + ) : base(logger) + { + } + + // + #region Hub Actions ... + /// + /// Add Specified Terms and Conditions ... + /// + /// + /// + /// + public async Task Add( + XStringDto entity, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.Add.GetStringValue(), + entity.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Deleted Specified Terms and Conditions ... + /// + /// + /// + /// + public async Task Delete( + XStringDto entity, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.Delete.GetStringValue(), + entity.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + + /// + /// Notify an Entity Updated ... + /// + /// + /// + /// + public async Task Update( + XStringDto entity, + CancellationToken cancellationToken = default + ) + { + // + var connectionId = Context.ConnectionId; + if (!connectionId.IsNullOrEmpty()) + { + // + await Clients + .AllExcept(connectionId) + .SendAsync( + XBaseEntityHubAction.Update.GetStringValue(), + entity.ToJSON(camelCase: true), + connectionId, + cancellationToken + ); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Interfaces\IXTermsAndConditionsController.cs + +using xStringService.Interfaces; + +namespace xTermsAndConditionsService.Interfaces +{ + /// + /// an Interface for Describing Terms and Conditions Management Controller Actions ... + /// + public interface IXTermsAndConditionsController : IXBaseResourceProviderController + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Interfaces\IXTermsAndConditionsProvider.cs + +using xStringService.Interfaces; + +namespace xTermsAndConditionsService.Interfaces +{ + /// + /// an Interface for Describing Terms and Conditions Management Provided Actions ... + /// + public interface IXTermsAndConditionsProvider : IXResourceProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xTermsAndConditionsService\Providers\XTermsAndConditionsProvider.cs + +using xStringService.Interfaces.Dtos; +using xStringService.Providers; +using xTermsAndConditionsService.Configurations; +using xTermsAndConditionsService.Interfaces; + +namespace xTermsAndConditionsService.Providers +{ + /// + /// an Implementation of Terms and Conditions Management Provided Actions ... + /// + public class XTermsAndConditionsProvider : XBaseResourceProvider, IXTermsAndConditionsProvider + { + public XTermsAndConditionsProvider( + IXStringServiceProvider provider, + XTermsAndConditionsServiceConfiguration configuration + ) : base( + resource: "NULL", + provider + ) + { + // + // Since we nee to Set Resource Title of Terms and Conditions + // during instancing using Provided Configurations ... + // set this value here ... + resource = configuration.ResourceTitle; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Configurations\Entities\XWebinarEntityConfiguration.cs + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xDataService.Providers; +using xWebinarService.Constants; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Configurations.Entities +{ + public class XWebinarEntityConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable(XWebinarServiceConstants.XWebinarTable); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Configurations\Entities\XWebinarSubscriberEntityConfiguration.cs + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using xDataService.Providers; +using xWebinarService.Constants; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Configurations.Entities +{ + public class XWebinarSubscriberEntityConfiguration : XBaseEntityTypeConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + builder.ToTable(XWebinarServiceConstants.XWebinarSubscriberTable); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Constants\XWebinarServiceConstants.cs + +namespace xWebinarService.Constants +{ + public struct XWebinarServiceConstants + { + // + // Service Extensions Log Tag ... + public const string XWebinarServiceDILogTag = "XWebinarService"; + + // + // Table Mapping Identifiers ... + public const string XWebinarTable = "Webinars"; + public const string XWebinarSubscriberTable = "WebinarSubscribers"; + + // + // GraphQL Collection Mappings ... + public const string XWebinarSingleName = "webinar"; + public const string XWebinarCollectionName = "webinars"; + + public const string XWebinarSubscriberSingleName = "webinarSubscriber"; + public const string XWebinarSubscriberCollectionName = "webinarSubscribers"; + + // + // Hubs ... + public const string XWebinarDtoHub = "webinarDto"; + public const string XWebinarEntityHub = "webinarEntity"; + public const string XWebinarSubscriberDtoHub = "webinarSubscriberDto"; + public const string XWebinarSubscriberEntityHub = "webinarSubscriberEntity"; + + // + // Custome Constants ... + + // + // EF Repositories Helper Extensions ... + public const string GetXWebinarEFRepositoryDescriptor = "GetXWebinarEFRepositoryDescriptor"; + public const string GetXWebinarSubscriberEFRepositoryDescriptor = "GetXWebinarSubscriberEFRepositoryDescriptor"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Constants\XWebinarType.cs + +using xExceptions.Attributes; + +namespace xWebinarService.Constants +{ + /// + /// Webinar Type ... + /// + public enum XWebinarType + { + [StringValue("Public")] + Public = 0, + [StringValue("Private")] + Private = 1 + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\DI\XDIHelperExtension.cs + +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xCommons.Helpers; +using xDataService.Constants; +using xDataService.Db; +using xDataService.DI; +using xDataService.Models; +using xExceptions.Constants; +using xPushService.DI; +using xPushService.Helpers; +using xWebinarService.Constants; +using xWebinarService.Helpers; +using xWebinarService.Interfaces.Dtos; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Providers.Dtos; +using xWebinarService.Providers.Entities; + +namespace xWebinarService.DI +{ + public static class XDIHelperExtension + { + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXWebinarService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + AddWebinarService( + contextType: null, + services: services, + lifeTime: lifeTime, + repositoryType: repositoryType + ); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + public static void AddXWebinarService( + this IServiceCollection services, + XRepositoryType repositoryType, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + where TContext : XDbContext + { + AddWebinarService( + services: services, + lifeTime: lifeTime, + contextType: typeof(TContext), + repositoryType: repositoryType + ); + } + + /// + /// Use XWebinarService Middleware ... + /// + /// + /// + public static void UsexWebinarService( + this IApplicationBuilder app, + bool isDevelopmentEnvironment = false + ) + { + // + #region Using XWebinar Hubs ... + // + var pushHelper = new XPushServiceHelper(); + + // + pushHelper.AddHub(XWebinarServiceConstants.XWebinarDtoHub); + pushHelper.AddHub(XWebinarServiceConstants.XWebinarEntityHub); + + // + pushHelper.AddHub(XWebinarServiceConstants.XWebinarSubscriberDtoHub); + pushHelper.AddHub(XWebinarServiceConstants.XWebinarSubscriberEntityHub); + + // + app.UseXPushService(pushHelper); + #endregion + + // + // Using XWebinar Repository Descriptor ... + if (!webinarDescriptor.IsNull()) + { + // + app.UseXRepository( + descriptor: webinarDescriptor, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + + // + // Using XWebinarSubscriber Repository Descriptor ... + if (!webinarSubscriberDescriptor.IsNull()) + { + // + app.UseXRepository( + descriptor: webinarSubscriberDescriptor, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + } + + // + #region Private ... + private static XRepositoryDescriptor webinarDescriptor = null; + private static XRepositoryDescriptor webinarSubscriberDescriptor = null; + + /// + /// a LogTag for Service ... + /// + private static string XLogTag = XWebinarServiceConstants.XWebinarServiceDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Service ... + /// + /// + /// + /// + /// + private static void AddWebinarService( + 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: + // + webinarDescriptor = typeof(XWebinarServiceHelper) + .InvokeGenericMethod( + args: null, + runtimeType: contextType, + methodName: XWebinarServiceConstants.GetXWebinarEFRepositoryDescriptor + ); + + // + webinarSubscriberDescriptor = typeof(XWebinarServiceHelper) + .InvokeGenericMethod( + args: null, + runtimeType: contextType, + methodName: XWebinarServiceConstants.GetXWebinarSubscriberEFRepositoryDescriptor + ); + break; + + // + case XRepositoryType.Mongo: + webinarDescriptor = XWebinarServiceHelper.GetXWebinarMongoRepositoryDescriptor(); + webinarSubscriberDescriptor = XWebinarServiceHelper.GetXWebinarSubscriberMongoRepositoryDescriptor(); + break; + + // + case XRepositoryType.InMemory: + webinarDescriptor = XWebinarServiceHelper.GetXWebinarInMemoryRepositoryDescriptor(); + webinarSubscriberDescriptor = XWebinarServiceHelper.GetXWebinarSubscriberInMemoryRepositoryDescriptor(); + break; + } + + // + // Register xWebinar Repository Descriptor ... + services.AddXRepository( + lifeTime: lifeTime, + descriptor: webinarDescriptor + ); + + services.AddXRepository( + lifeTime: lifeTime, + descriptor: webinarSubscriberDescriptor + ); + + // + // Register Dto Services ... + services.Add( + new ServiceDescriptor(typeof(IXWebinarServiceProvider), typeof(XWebinarServiceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXWebinarRepositoryService), typeof(XWebinarRepositoryService), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXWebinarRepositoryProvider), typeof(XWebinarRepositoryProvider), lifeTime) + ); + + services.Add( + new ServiceDescriptor(typeof(IXWebinarSubscriberServiceProvider), typeof(XWebinarSubscriberServiceProvider), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXWebinarSubscriberRepositoryService), typeof(XWebinarSubscriberRepositoryService), lifeTime) + ); + services.Add( + new ServiceDescriptor(typeof(IXWebinarSubscriberRepositoryProvider), typeof(XWebinarSubscriberRepositoryProvider), lifeTime) + ); + + // + // services.Add( + // new ServiceDescriptor(typeof(IXWebinarProvider), typeof(XWebinarProvider), lifeTime) + // ); + + // + Log("Service Regitration Succeed ..."); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Helpers\XWebinarServiceHelper.cs + +using System; +using GraphQL.Types; +using xDataService.Db; +using xDataService.Models; +using xDataService.Providers; +using xWebinarService.Interfaces; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; +using xWebinarService.Providers.Entities; +using xWebinarService.Providers.GraphQL; + +namespace xWebinarService.Helpers +{ + public static class XWebinarServiceHelper + { + // + #region XWebinar ... + // + #region EF Repository Descriptor Provider(s) ... + public static XEFRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarEFRepository, + TContext + > GetXWebinarEFRepositoryDescriptor() + where TContext : XDbContext + { + // + var result = new XEFRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarEFRepository, + TContext + >(); + + // + return result; + } + + public static XEFRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarEFRepository, + TContext, + IXWebinarSeeder, + TSeederImplementation + > GetXWebinarEFRepositoryDescriptor() + where TContext : XDbContext + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XEFRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarEFRepository, + TContext, + IXWebinarSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region Mongo Repository Descriptor Provider(s) ... + public static XMongoRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarMongoRepository + > GetXWebinarMongoRepositoryDescriptor() + { + // + var result = new XMongoRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarMongoRepository + >(); + + // + return result; + } + + public static XMongoRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarMongoRepository, + IXWebinarSeeder, + TSeederImplementation + > GetXWebinarMongoRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XMongoRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarMongoRepository, + IXWebinarSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region InMemory Repository Descriptor Provider(s) ... + public static XInMemoryRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarInMemoryRepository + > GetXWebinarInMemoryRepositoryDescriptor() + { + // + var result = new XInMemoryRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarInMemoryRepository + >(); + + // + return result; + } + + public static XInMemoryRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarInMemoryRepository, + IXWebinarSeeder, + TSeederImplementation + > GetXWebinarInMemoryRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XInMemoryRepositoryDescriptor< + XWebinar, + Guid, + IXWebinarKeyGenerator, + XWebinarKeyGenerator, + IXWebinarRepositoryEvents, + XWebinarRepositoryEvents, + XWebinarGraphType, + GuidGraphType, + XWebinarGraphQuery, + IXWebinarGraphQLTypeHelper, + XWebinarGraphQLTypeHelper, + XWebinarGraphSchema, + IXWebinarRepository, + XWebinarInMemoryRepository, + IXWebinarSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + #endregion + + // + #region XWebinarSubscriber ... + // + #region EF Repository Descriptor Provider(s) ... + public static XEFRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberEFRepository, + TContext + > GetXWebinarSubscriberEFRepositoryDescriptor() + where TContext : XDbContext + { + // + var result = new XEFRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberEFRepository, + TContext + >(); + + // + return result; + } + + public static XEFRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberEFRepository, + TContext, + IXWebinarSubscriberSeeder, + TSeederImplementation + > GetXWebinarSubscriberEFRepositoryDescriptor() + where TContext : XDbContext + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XEFRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberEFRepository, + TContext, + IXWebinarSubscriberSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region Mongo Repository Descriptor Provider(s) ... + public static XMongoRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberMongoRepository + > GetXWebinarSubscriberMongoRepositoryDescriptor() + { + // + var result = new XMongoRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberMongoRepository + >(); + + // + return result; + } + + public static XMongoRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberMongoRepository, + IXWebinarSubscriberSeeder, + TSeederImplementation + > GetXWebinarSubscriberMongoRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XMongoRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberMongoRepository, + IXWebinarSubscriberSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + + // + #region InMemory Repository Descriptor Provider(s) ... + public static XInMemoryRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberInMemoryRepository + > GetXWebinarSubscriberInMemoryRepositoryDescriptor() + { + // + var result = new XInMemoryRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberInMemoryRepository + >(); + + // + return result; + } + + public static XInMemoryRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberInMemoryRepository, + IXWebinarSubscriberSeeder, + TSeederImplementation + > GetXWebinarSubscriberInMemoryRepositoryDescriptor() + where TSeederImplementation : XBaseDbSeeder + { + // + var result = new XInMemoryRepositoryDescriptor< + XWebinarSubscriber, + int, + IXWebinarSubscriberKeyGenerator, + XWebinarSubscriberKeyGenerator, + IXWebinarSubscriberRepositoryEvents, + XWebinarSubscriberRepositoryEvents, + XWebinarSubscriberGraphType, + IntGraphType, + XWebinarSubscriberGraphQuery, + IXWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphQLTypeHelper, + XWebinarSubscriberGraphSchema, + IXWebinarSubscriberRepository, + XWebinarSubscriberInMemoryRepository, + IXWebinarSubscriberSeeder, + TSeederImplementation + >(); + + // + return result; + } + #endregion + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Dtos\IXWebinarDtoHub.cs + +using System; +using xPushService.Interfaces; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Dtos +{ + public interface IXWebinarDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Dtos\IXWebinarRepositoryService.cs + +using System; +using xDataService.Interfaces; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Dtos +{ + public interface IXWebinarRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Dtos\IXWebinarServiceProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using xPushService.Interfaces; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; +using xWebinarService.Providers.Dtos; + +namespace xWebinarService.Interfaces.Dtos +{ + public interface IXWebinarServiceProvider : IXBaseDtoProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Dtos\IXWebinarSubscriberDtoHub.cs + +using xPushService.Interfaces; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Dtos +{ + public interface IXWebinarSubscriberDtoHub : IXBaseDtoHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Dtos\IXWebinarSubscriberRepositoryService.cs + +using xDataService.Interfaces; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Dtos +{ + public interface IXWebinarSubscriberRepositoryService : IXBaseRepositoryService + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Dtos\IXWebinarSubscriberServiceProvider.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace xWebinarService.Interfaces.Dtos +{ + public interface IXWebinarSubscriberServiceProvider + { + + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarEntityHub.cs + +using System; +using xPushService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarGraphQLTypeHelper.cs + +using System; +using xDataService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarKeyGenerator.cs + +using System; +using xDataService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarRepository.cs + +using System; +using xDataService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarRepositoryEvents.cs + +using xDataService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarRepositoryProvider.cs + +using System; +using xPushService.Interfaces; +using xWebinarService.Models.Entities; +using xWebinarService.Providers.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarSeeder.cs + +using System; +using xDataService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarSeeder : IXBaseDbSeeder + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarSubscriberEntityHub.cs + +using xPushService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarSubscriberEntityHub : IXBaseEntityHub + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarSubscriberGraphQLTypeHelper.cs + +using xDataService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarSubscriberGraphQLTypeHelper : IXBaseGraphQLTypeHelper + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarSubscriberKeyGenerator.cs + +using xDataService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarSubscriberKeyGenerator : IXKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarSubscriberRepository.cs + +using xDataService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarSubscriberRepository : IXBaseRepository + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarSubscriberRepositoryEvents.cs + +using xDataService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarSubscriberRepositoryEvents : IXBaseRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarSubscriberRepositoryProvider.cs + +using xPushService.Interfaces; +using xWebinarService.Models.Entities; +using xWebinarService.Providers.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarSubscriberRepositoryProvider : IXBaseEntityProvider + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Interfaces\Entities\IXWebinarSubscriberSeeder.cs + +using xDataService.Interfaces; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces.Entities +{ + public interface IXWebinarSubscriberSeeder : IXBaseDbSeeder + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Models\Dtos\XWebinarDto.cs + +using System; +using xModels.Base; +using xModels.Dtos; +using xWebinarService.Constants; + +namespace xWebinarService.Models.Dtos +{ + public class XWebinarDto : XBaseGuidIDEntityDto + { + public bool Enabled { get; set; } + public string OwnerId { get; set; } + public XPersonDto Owner { get; set; } + public XWebinarType Type { get; set; } + public XResourceDto Title { get; set; } + public DateTime CreatedOn { get; set; } + public XResourceDto Description { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Models\Dtos\XWebinarSubscriberDto.cs + +using System; +using xModels.Base; +using xModels.Dtos; + +namespace xWebinarService.Models.Dtos +{ + public class XWebinarSubscriberDto : XBaseIntIDEntityDto + { + public Guid WebinarId { get; set; } + public string SubscriberId { get; set; } + public XPersonDto Subscriber { get; set; } + public DateTime SubscribedOn { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Models\Entities\XWebinar.cs + +using System; +using System.ComponentModel.DataAnnotations; +using xModels.Base; +using xWebinarService.Constants; + +namespace xWebinarService.Models.Entities +{ + /// + /// Webinar Entity ... + /// + public class XWebinar : XBaseGuidIDEntity + { + /// + /// Title (Resource ID) ... + /// + /// + [Required] + [StringLength(255)] + public string Title { get; set; } + + /// + /// Description (Resource ID) ... + /// + /// + [Required] + [StringLength(255)] + public string Description { get; set; } + + /// + /// User Identifier ... + /// + /// + [Required] + public string OwnerId { get; set; } + + /// + /// Webinar Type ... + /// + public XWebinarType Type { get; set; } + + /// + /// Creation Time ... + /// + /// + [Required] + public DateTime CreatedOn { get; set; } + + /// + /// Specified Webinar Enabled or not ... + /// + /// + public bool Enabled { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Models\Entities\XWebinarSubscriber.cs + +using System; +using System.ComponentModel.DataAnnotations; +using xModels.Base; + +namespace xWebinarService.Models.Entities +{ + /// + /// Webinar Subscriber ... + /// + public class XWebinarSubscriber : XBaseIntIDEntity + { + [Required] + public string SubscriberId { get; set; } + + [Required] + public Guid WebinarId { get; set; } + + public DateTime SubscribedOn { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Dtos\XWebinarDtoHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xPushService.Base; +using xWebinarService.Interfaces.Dtos; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Dtos +{ + public class XWebinarDtoHub : XBaseDtoHub, IXWebinarDtoHub + { + public XWebinarDtoHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Dtos\XWebinarRepositoryService.cs + +using System; +using System.Collections.Generic; +using AutoMapper; +using xDataService.Providers; +using xWebinarService.Interfaces.Dtos; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Dtos +{ + public class XWebinarRepositoryService : XBaseRepositoryService, IXWebinarRepositoryService + { + public XWebinarRepositoryService( + IXWebinarRepository repository, + IEnumerable mapperProfiles = null + ) : base( + repository, + mapperProfiles + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Dtos\XWebinarServiceProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; +using xWebinarService.Interfaces.Dtos; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Dtos +{ + public class XWebinarServiceProvider : XBaseDtoProvider, IXWebinarServiceProvider + { + public XWebinarServiceProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXWebinarRepositoryService service, + IXIdentityProvider identityProvider = null + ) : base( + hub, + dataConfiguration, + service, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Dtos\XWebinarSubscriberDtoHub.cs + +using Microsoft.Extensions.Logging; +using xPushService.Base; +using xWebinarService.Interfaces.Dtos; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Dtos +{ + public class XWebinarSubscriberDtoHub : XBaseDtoHub, IXWebinarSubscriberDtoHub + { + public XWebinarSubscriberDtoHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Dtos\XWebinarSubscriberRepositoryService.cs + +using System.Collections.Generic; +using AutoMapper; +using xDataService.Providers; +using xWebinarService.Interfaces.Dtos; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Dtos +{ + public class XWebinarSubscriberRepositoryService : XBaseRepositoryService, IXWebinarSubscriberRepositoryService + { + public XWebinarSubscriberRepositoryService( + IXWebinarSubscriberRepository repository, + IEnumerable mapperProfiles = null + ) : base( + repository, + mapperProfiles + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Dtos\XWebinarSubscriberServiceProvider.cs + +using Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; +using xWebinarService.Interfaces.Dtos; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Dtos +{ + public class XWebinarSubscriberServiceProvider : XBaseDtoProvider, IXWebinarSubscriberServiceProvider + { + public XWebinarSubscriberServiceProvider( + IHubContext hub, + XDataServiceConfiguration dataConfiguration, + IXWebinarSubscriberRepositoryService service, + IXIdentityProvider identityProvider = null + ) : base( + hub, + dataConfiguration, + service, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarEFRepository.cs + +using System; +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarEFRepository : XBaseEFRepository, IXWebinarRepository + where TContext : XDbContext + { + public XWebinarEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXWebinarKeyGenerator keyGenerator = null, + IXWebinarRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarEntityHub.cs + +using System; +using Microsoft.Extensions.Logging; +using xPushService.Base; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarEntityHub : XBaseEntityHub, IXWebinarEntityHub + { + public XWebinarEntityHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarInMemoryRepository.cs + +using System; +using xDataService.Configuration; +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarInMemoryRepository : XBaseInMemoryRepository, IXWebinarRepository + { + public XWebinarInMemoryRepository( + XDataServiceConfiguration configuration, + IXWebinarKeyGenerator keyGenerator = null, + IXWebinarRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarKeyGenerator.cs + +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarKeyGenerator : XGuidKeyGenerator, IXWebinarKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarMongoRepository.cs + +using System; +using xDataService.Configuration; +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarMongoRepository : XBaseMongoRepository, IXWebinarRepository + { + public XWebinarMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXWebinarKeyGenerator keyGenerator = null, + IXWebinarRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarRepositoryEvents.cs + +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarRepositoryEvents : XBaseRepositoryEvents, IXWebinarRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarRepositoryProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarRepositoryProvider : XBaseEntityProvider, IXWebinarRepositoryProvider + { + public XWebinarRepositoryProvider( + IHubContext hub, + IXWebinarRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + repository, + dataConfiguration, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarSeederBase.cs + +using System; +using xDataService.Configuration; +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public abstract class XWebinarSeederBase : XBaseDbSeeder, IXWebinarSeeder + { + public XWebinarSeederBase( + string entity, + string database, + IXWebinarRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + entity, + database, + repository, + dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarSubscriberEFRepository.cs + +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Interfaces; +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarSubscriberEFRepository : XBaseEFRepository, IXWebinarSubscriberRepository + where TContext : XDbContext + { + public XWebinarSubscriberEFRepository( + IXUnitOfWorks unitOfWorks, + XDataServiceConfiguration configuration, + IXWebinarSubscriberKeyGenerator keyGenerator = null, + IXWebinarSubscriberRepositoryEvents baseRepositoryEvents = null + ) : base( + unitOfWorks, + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarSubscriberEntityHub.cs + +using Microsoft.Extensions.Logging; +using xPushService.Base; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarSubscriberEntityHub : XBaseEntityHub, IXWebinarSubscriberEntityHub + { + public XWebinarSubscriberEntityHub( + ILogger logger + ) : base(logger) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarSubscriberInMemoryRepository.cs + +using xDataService.Configuration; +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarSubscriberInMemoryRepository : XBaseInMemoryRepository, IXWebinarSubscriberRepository + { + public XWebinarSubscriberInMemoryRepository( + XDataServiceConfiguration configuration, + IXWebinarSubscriberKeyGenerator keyGenerator = null, + IXWebinarSubscriberRepositoryEvents baseRepositoryEvents = null + ) : base( + configuration, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarSubscriberKeyGenerator.cs + +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarSubscriberKeyGenerator : XIntKeyGenerator, IXWebinarSubscriberKeyGenerator + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarSubscriberMongoRepository.cs + +using xDataService.Configuration; +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarSubscriberMongoRepository : XBaseMongoRepository, IXWebinarSubscriberRepository + { + public XWebinarSubscriberMongoRepository( + XDataBaseConfiguration dbConfiguration, + XDataServiceConfiguration configuration, + string collectionName = null, + IXWebinarSubscriberKeyGenerator keyGenerator = null, + IXWebinarSubscriberRepositoryEvents baseRepositoryEvents = null + ) : base( + dbConfiguration, + configuration, + collectionName, + keyGenerator, + baseRepositoryEvents + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarSubscriberRepositoryEvents.cs + +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarSubscriberRepositoryEvents : XBaseRepositoryEvents, IXWebinarSubscriberRepositoryEvents + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarSubscriberRepositoryProvider.cs + +using System; +using Microsoft.AspNetCore.SignalR; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xPushService.Base; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public class XWebinarSubscriberRepositoryProvider : XBaseEntityProvider, IXWebinarSubscriberRepositoryProvider + { + public XWebinarSubscriberRepositoryProvider( + IHubContext hub, + IXWebinarSubscriberRepository repository, + XDataServiceConfiguration dataConfiguration, + IXIdentityProvider identityProvider = null + ) : base( + hub, + repository, + dataConfiguration, + identityProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\Entities\XWebinarSubscriberSeederBase.cs + +using xDataService.Configuration; +using xDataService.Providers; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.Entities +{ + public abstract class XWebinarSubscriberSeederBase : XBaseDbSeeder, IXWebinarSubscriberSeeder + { + public XWebinarSubscriberSeederBase( + string entity, + string database, + IXWebinarSubscriberRepository repository, + XDataServiceConfiguration dataServiceConfiguration + ) : base( + entity, + database, + repository, + dataServiceConfiguration + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\GraphQL\XWebinarGraphQLTypeHelper.cs + +using System; +using xDataService.Configuration; +using xDataService.GraphQL; +using xWebinarService.Constants; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.GraphQL +{ + public class XWebinarGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXWebinarGraphQLTypeHelper + { + public XWebinarGraphQLTypeHelper( + XDataServiceConfiguration configuration + ) : base(configuration) + { } + + public override string GetInQueryCollectionName() + { + return XWebinarServiceConstants.XWebinarCollectionName; + } + + public override string GetInQuerySingleName() + { + return XWebinarServiceConstants.XWebinarSingleName; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\GraphQL\XWebinarGraphQuery.cs + +using System; +using GraphQL.Types; +using xDataService.Configuration; +using xDataService.GraphQL; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.GraphQL +{ + public class XWebinarGraphQuery : XBaseGraphQLQuery + { + public XWebinarGraphQuery( + XDataServiceConfiguration configuration, + IXWebinarRepository repository, + IXWebinarGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\GraphQL\XWebinarGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xWebinarService.Providers.GraphQL +{ + public class XWebinarGraphSchema : Schema + { + public XWebinarGraphSchema(IServiceProvider services) : base(services) + { + Query = (XWebinarGraphQuery)services.GetService(typeof(XWebinarGraphQuery)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\GraphQL\XWebinarGraphType.cs + +using System; +using xDataService.GraphQL; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.GraphQL +{ + public class XWebinarGraphType : XBaseGraphObjectType + { + public XWebinarGraphType() : base() + { + Field(x => x.Type); + Field(x => x.Title); + Field(x => x.OwnerId); + Field(x => x.Enabled); + Field(x => x.CreatedOn); + Field(x => x.Description); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\GraphQL\XWebinarSubscriberGraphQLTypeHelper.cs + +using System; +using xDataService.Configuration; +using xDataService.GraphQL; +using xWebinarService.Constants; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.GraphQL +{ + public class XWebinarSubscriberGraphQLTypeHelper : XBaseGraphQLTypeHelper, IXWebinarSubscriberGraphQLTypeHelper + { + public XWebinarSubscriberGraphQLTypeHelper( + XDataServiceConfiguration configuration + ) : base(configuration) + { } + + public override string GetInQueryCollectionName() + { + return XWebinarServiceConstants.XWebinarSubscriberCollectionName; + } + + public override string GetInQuerySingleName() + { + return XWebinarServiceConstants.XWebinarSubscriberSingleName; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\GraphQL\XWebinarSubscriberGraphQuery.cs + +using GraphQL.Types; +using xDataService.Configuration; +using xDataService.GraphQL; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.GraphQL +{ + public class XWebinarSubscriberGraphQuery : XBaseGraphQLQuery + { + public XWebinarSubscriberGraphQuery( + XDataServiceConfiguration configuration, + IXWebinarSubscriberRepository repository, + IXWebinarSubscriberGraphQLTypeHelper helper + ) : base( + configuration, + repository, + helper + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\GraphQL\XWebinarSubscriberGraphSchema.cs + +using System; +using GraphQL.Types; + +namespace xWebinarService.Providers.GraphQL +{ + public class XWebinarSubscriberGraphSchema : Schema + { + public XWebinarSubscriberGraphSchema(IServiceProvider services) : base(services) + { + Query = (XWebinarSubscriberGraphQuery)services.GetService(typeof(XWebinarSubscriberGraphQuery)); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\GraphQL\XWebinarSubscriberGraphType.cs + +using xDataService.GraphQL; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers.GraphQL +{ + public class XWebinarSubscriberGraphType : XBaseGraphObjectType + { + public XWebinarSubscriberGraphType() : base() + { + Field(x => x.WebinarId); + Field(x => x.SubscribedOn); + Field(x => x.SubscriberId); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\XWebinarEntityRegisterar.cs + +using System; +using Microsoft.EntityFrameworkCore; +using xDataService.Interfaces; +using xDataService.Providers; +using xWebinarService.Configurations.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers +{ + public class XWebinarEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar + { + public XWebinarEntityRegisterar() + : base(nameof(XWebinarEntityRegisterar)) + { + // + // Add Entity ... + AddEntity(); + } + + /// + /// Configure Entities ... + /// + /// + public override void ConfigureEntities(ModelBuilder modelBuilder) + { + // + // Configure Entity Using Provided ... + modelBuilder + .ApplyConfigurationsFromAssembly(typeof(XWebinarEntityConfiguration).Assembly); + + // + base.ConfigureEntities(modelBuilder); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xWebinarService\Providers\XWebinarSubscriberEntityRegisterar.cs + +using Microsoft.EntityFrameworkCore; +using xDataService.Interfaces; +using xDataService.Providers; +using xWebinarService.Configurations.Entities; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Providers +{ + public class XWebinarSubscriberEntityRegisterar : XBaseEntityRegisterar, IXEntityRegisterar + { + public XWebinarSubscriberEntityRegisterar() + : base(nameof(XWebinarSubscriberEntityRegisterar)) + { + // + // Add Entity ... + AddEntity(); + } + + /// + /// Configure Entities ... + /// + /// + public override void ConfigureEntities(ModelBuilder modelBuilder) + { + // + // Configure Entity Using Provided ... + modelBuilder + .ApplyConfigurationsFromAssembly(typeof(XWebinarSubscriberEntityConfiguration).Assembly); + + // + base.ConfigureEntities(modelBuilder); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xAiApi\Constants\XAiApiConstants.cs + +namespace xAiApi.Constants +{ + public struct XAiApiConstants + { + // + // Database Descriptor ... + public const string XAiApiDbIdentifier = "xAiApiDb"; + public const string XAiApiMigrationsAssemblyIdentifier = "xAiApi"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xAiApi\Controllers\StartupController.cs + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Controllers; +using xCommons.Providers; + +namespace xAiApi.Controllers +{ + /// + /// Runing when application start ... + /// + [Route("")] + [AllowAnonymous] + public class StartupController : XBaseController + { + + public StartupController( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { } + + /// + /// Show Configured Welcome Message + /// + /// + [HttpGet("")] + [AllowAnonymous] + public virtual ActionResult Index() + { + // + var controllerName = GetControllerName(); + var message = $"{AppConfiguration.WelcomeMessage}"; + + // + return Ok(message); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xAiApi\Database\XAiApiDatabaseDescriptor.cs + +using xAiApi.Constants; +using xDataService.Interfaces; +using xDataService.Models; + +namespace xAiApi.Database +{ + /// + /// Ai Api Database Descriptor ... + /// + public class XAiApiDatabaseDescriptor : XDatabaseDescriptor, IXDatabaseDescriptor + { + public XAiApiDatabaseDescriptor() : base( + name: XAiApiConstants.XAiApiDbIdentifier, + repositories: [] + ) + { + OptionsBuilder = b => b.MigrationsAssembly(XAiApiConstants.XAiApiMigrationsAssemblyIdentifier); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xAiApi\Database\XAiApiDbContext.cs + +using Microsoft.EntityFrameworkCore; +using xAiApi.Constants; +using xAiModels.Providers; +using xDataService.Configuration; +using xDataService.Db; +using xDataService.Providers; +using xFileService.Providers; +using xStringService.Providers; +using xTagService.Providers; + +namespace xAiApi.Database +{ + /// + /// Db Context of AiApi ... + /// + public class XAiApiDbContext : XDbContext + { + // + #region DbSets ... + #endregion + + // + #region Navigation Property Entities ... + #endregion + + // + #region Constructor ... + public XAiApiDbContext( + DbContextOptions options, + XDataServiceConfiguration config, + XEntityRegisterarHandlerService dynamicEntityHandlers + ) : base( + config: config, + options: options, + database: XAiApiConstants.XAiApiDbIdentifier, + dynamicEntityHandlers: dynamicEntityHandlers, + dynamicEntityRegisterarsNames: [ + nameof(XTagEntityRegisterar), + nameof(XFileEntityRegisterar), + nameof(XStringEntityRegisterar), + nameof(XAiMessageEntityRegisterar), + nameof(XAiProjectEntityRegisterar), + nameof(XAiConversationEntityRegisterar), + ] + ) + { } + #endregion + + // + #region Overrides ... + // + public override void OnXModelCreating(ModelBuilder modelBuilder) + { } + + // + public override void OnXConfiguring(DbContextOptionsBuilder optionsBuilder) + { } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xAiApi\Extensions\XProgramExtensions.cs + +using System.Collections.Generic; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Authorization; +using xCommons.Extensions; +using xIdentityHelper; + +namespace xAiApi.Extensions +{ + public static class XProgramExtensions + { + /// + /// Register Authorization Policies + /// + /// + /// + public static void AddXAuthorization( + this IServiceCollection services, + IDictionary policies = null + ) + { + // + services.AddAuthorization(options => + { + // + if (!policies.IsNull()) + { + // + // Fill Policies ... + using (var policiesEnumerator = policies.GetEnumerator()) + { + while (policiesEnumerator.MoveNext()) + { + var policyDescriptor = policiesEnumerator.Current; + options.AddPolicy(policyDescriptor.Key, policyDescriptor.Value); + } + } + } + + // + var xPolicies = XAuthorizationHelper.GetXAuthorizationPolicies(); + if (!xPolicies.IsNull()) + { + // + // Fill Policies ... + using (var policiesEnumerator = xPolicies.GetEnumerator()) + { + while (policiesEnumerator.MoveNext()) + { + var policyDescriptor = policiesEnumerator.Current; + options.AddPolicy(policyDescriptor.Key, policyDescriptor.Value); + } + } + } + }); + + // + services.AddSingleton(); + } + + /// + /// Use Forward Headers Options for resolving behind a proxy issues ... + /// + /// + /// + public static void UseXForwardOptions(this IApplicationBuilder source) + { + // + var forwardOptions = new ForwardedHeadersOptions + { + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto, + RequireHeaderSymmetry = false + }; + + // + forwardOptions.KnownNetworks.Clear(); + forwardOptions.KnownProxies.Clear(); + + // + // ref: https://github.com/aspnet/Docs/issues/2384 + source.UseForwardedHeaders(forwardOptions); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xAiApi\Interfaces\IXAIApiServiceBase.cs + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.AI; + +namespace xAiApi.Interfaces +{ + /// + /// Describe a Base Service Actions of any Ai Based Services ... + /// + public interface IAIServiceBase : IDisposable + { + /// + /// Base Method for Communicate with LLM ... + /// + /// + /// + /// + Task AskAsync( + string prompt, + CancellationToken cancellationToken = default + ); + + /// + /// Base Method for Communicate with LLM ... + /// + /// + /// + /// + IAsyncEnumerable AskAsEnumerable( + string prompt, + CancellationToken cancellationToken = default + ); + + /// + /// Get LLM Client instance for Communicating with LLM ... + /// + /// + /// + IChatClient GetClient(string llm = null); + + /// + /// Create Custom HttpClient for Communicating with LLM API ... + /// + /// + /// + HttpClient GetHttpClient(string url = null); + + /// + /// Prepare a Message History List for LLM Communication based on Prompt ... + /// + /// + /// + /// + IList GetHistory( + string prompt = null, + bool forcePrompt = true + ); + + /// + /// Prepare a Message History List for LLM Communication based on Prompt ... + /// + /// + /// + /// + IList GetHistory( + ChatMessage message = null, + bool forceMessage = true + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xAiApi\Program.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace xAiApi +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xAiApi\Startup.cs + +using System; +using System.IO; +using System.Linq; +using xTagService.DI; +using System.Net.Http; +using xAiApi.Database; +using xHttpService.DI; +using xPushService.DI; +using xFileService.DI; +using xDataService.DI; +using Newtonsoft.Json; +using System.Reflection; +using xAiApi.Extensions; +using xStringService.DI; +using xStorageService.DI; +using xIdentityService.DI; +using xDataService.Models; +using xCommons.Extensions; +using xCommons.Configurations; +using System.Collections.Generic; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json.Serialization; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using IdentityModel.AspNetCore.OAuth2Introspection; +using xPushService.Helpers; + +namespace xAiApi +{ + public class Startup + { + // + #region Props ... + public IConfiguration Configuration { get; } + private readonly List databases; + private readonly XAiApiDatabaseDescriptor apiDatabaseDescriptor; + #endregion + + // + #region Constructor(s) ... + public Startup(IConfiguration configuration) + { + // + Configuration = configuration; + + // + // Create an Api Descriptor Class Instance ... + apiDatabaseDescriptor = new XAiApiDatabaseDescriptor(); + databases = new List + { + apiDatabaseDescriptor + }; + } + #endregion + + public void ConfigureServices(IServiceCollection services) + { + // + // Register Validation Provider and all XCommons Module Services ... + services.AddXCommons(); + + // + // Register App Configuration ... + services.AddXAppConfiguration(Configuration); + var appConfiguration = services.GetRegisteredService(); + + // + // Register Allowed Origins ... + services.AddXCors(appConfiguration.AllowedOrigins); + + // + // Register Swagger ... + var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + services.AddXSwagger(Configuration, xmlFilePath: xmlPath); + + // + // Register HttpService ... + services.AddXHttpService(Configuration); + + // + // Add Api V1 Versioning ... + services.AddXApiV1Versioning(); + + // + // OAuthIntrospectin Http Client Handler ... + // this is for handling SSLErrors ... + services.AddHttpClient(OAuth2IntrospectionDefaults.BackChannelHttpClientName) + .ConfigurePrimaryHttpMessageHandler(() => + { + return new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true + }; + }); + + // + // Register Authorizations ... + services.AddXAuthorization(); + + // + services.AddControllers() + .AddNewtonsoftJson(x => + { + x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + x.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + }); + + // + var lifeTime = ServiceLifetime.Scoped; + var repositoryType = xDataService.Constants.XRepositoryType.EF; + + // + // Register XIdentity Service ... + services.AddXIdentityService( + lifeTime: lifeTime, + configuration: Configuration + ); + + // + // Storage Service ... + services.AddXStorageService(Configuration); + + // + // Push Service ... + services.AddXPushService( + lifeTime: lifeTime, + config: Configuration + ); + + // + // Preparing DataBases ... + if (databases.HasChild()) + { + // + databases + .ToList() + .ForEach(database => + { + // + // Register DataBase ... + services.AddXDatabase( + lifeTime: lifeTime, + descriptor: database, + configuration: Configuration + ); + }); + } + + // + // Register String Service ... + services.AddXStringService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + + // + // Register Tag Service ... + services.AddXTagService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + + // + // Register File Service ... + services.AddXFileService( + lifeTime: lifeTime, + repositoryType: repositoryType + ); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + // + // Configure the HTTP request pipeline. + var isDevelopmentEnvironment = false; + if (env.IsDevelopment()) + { + isDevelopmentEnvironment = true; + app.UseDeveloperExceptionPage(); + } + + // + // Use Swagger Middleware ... + app.UseXSwagger(); + + // + // Force Https Redirection ... + app.UseHttpsRedirection(); + + // + // Using Cors ... + app.UseXCors(); + + // + // Using Routing ... + app.UseRouting(); + + // + // Use xIdentityService ... + app.UseXIdentityService(); + + // + // Use Authorization ... + app.UseAuthorization(); + + // + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + + // + // Use Storage Service ... + app.UseXStorageService(); + + // + // Use PushService ... + var pushHelper = new XPushServiceHelper(); + app.UseXPushService(pushHelper); + + // + // Using DataBase ... + if (databases.HasChild()) + { + // + databases + .ToList() + .ForEach(database => + { + // + app.UseXDatabase( + descriptor: database, + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + }); + } + + // + // Using String Service ... + app.UseXStringService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + + // + // Using XTag Service ... + app.UseXTagService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + + // + // Using File Service ... + app.UseXFileService( + isDevelopmentEnvironment: isDevelopmentEnvironment + ); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\Account\AccountController+Admin.cs + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xIdentityHelper; +using xCommons.Attributes; +using xCommons.Extensions; +using xIdentityModels.Dtos; + +namespace xApi.Controllers { + public partial class AccountController { + // + #region Admin Actions ... + /// + /// Ban Specific Users + /// + /// an instance of XUserNameIdRequest which represents user identifier list to Ban + /// a list of banned users identifiers + [RequireXPowered] + [HttpPost ("Ban")] + [Authorize (Policy = XPolicies.EnabledAdmin)] + public async Task>> Ban ( + [FromBody] XUserNameIdRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder () + .AddNotNull (model) + .AddNotZeroChilds (model.Ids) + .ValidateGroupAsync (); + + // + var tokens = await RetrieveTokensAsXTokenResponse (); + var result = await identityProvider + .Ban ( + tokens, + model + ); + + // + return Ok (result) + .ToDynamicObject (); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// UnBann Specific Users + /// + /// an instance of XUserNameIdRequest which represents user identifier list to Ban + /// a list of unbanned users identifiers + [RequireXPowered] + [HttpPost ("UnBan")] + [Authorize (Policy = XPolicies.EnabledAdmin)] + public async Task>> UnBan ( + [FromBody] XUserNameIdRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder () + .AddNotNull (model) + .AddNotZeroChilds (model.Ids) + .ValidateGroupAsync (); + + // + var tokens = await RetrieveTokensAsXTokenResponse (); + var result = await identityProvider + .UnBan ( + tokens, + model + ); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// Check Specific User is Banned or not + /// + /// specified user's identifier + /// a boolean value which represent user banned or not + [RequireXPowered] + [HttpGet ("IsBanned/{userSelectByParam?}")] + [Authorize (Policy = XPolicies.EnabledAdmin)] + [Authorize (Policy = XPolicies.EnabledAgent)] + public async Task> IsBanned ( + [FromRoute] string userSelectByParam = null + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (userSelectByParam.IsNullOrEmpty ()) { + userSelectByParam = User.Identity.Name; + } + ValidationProvider.NotEmpty (userSelectByParam); + + // + var tokens = await RetrieveTokensAsXTokenResponse (); + var result = await identityProvider + .IsBanned ( + tokens, + userSelectByParam + ); + + // + return Ok (result); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\Account\AccountController+Authentication.cs + +using System; +using System.Threading.Tasks; +using IdentityModel.Client; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xIdentityHelper; +using xCommons.Attributes; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Models; +using xModels.Dtos; +using static xIdentityHelper.XApiScopeHelper; + +namespace xApi.Controllers { + public partial class AccountController { + // + #region Authentication Actions ... + /// + /// Retrieve OAuth Discovery Document + /// + /// an instance of DiscoveryDocumentResponse + [AllowAnonymous] + [RequireXPowered] + [HttpGet ("DiscoveryDocument")] + public async Task> GetDiscoveryDocument () { + // + try { + // + var result = await identityProvider + .RequestDiscoveryDocument (); + + // + // Ceck Response Result ... + if (result.IsError) { + XException.InvalidConfiguration.Throw (); + } + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + return result; + } + } + + /// + /// Request AccessToken for Specific XApiScope + /// + /// a member of XApiScope + /// an instance of XTokenResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("RequestScopeAccessToken")] + public async Task> RequestScopeAccessToken ( + [FromHeader] XApiScope scope + ) { + // + // Do Action ... + try { + // + // Validate Args ... + var scopeName = string.Empty; + try { + scopeName = scope.GetStringValue (); + } catch { } + ValidationProvider.NotEmpty (scopeName); + + // + var result = await identityProvider + .RequestScopeAccessToken (scopeName); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + return result; + } + } + + /// + /// Authenticate User + /// + /// + /// Sample request: + /// + /// { + /// "password": "", + /// "userSelectBy": "", + /// } + /// + /// + /// User Login Required Info, an instance of XLoginRequest + /// an instance of XTokenResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("Authenticate")] + public async Task> Authenticate ( + [FromBody] XLoginRequest model + ) { + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotNull (model) + .AddNotEmpty ( + model.UserSelectBy, + model.Password + ) + .ValidateGroupAsync (); + + // + var result = await identityProvider + .Authenticate (model); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + return result; + } + } + + /// + /// Authenticate User + /// + /// + /// Sample request: + /// + /// { + /// "language": "fa-IR" + /// "password": "", + /// "userSelectBy": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// User Login Required Info, an instance of XLoginRequest + /// an instance of XLoginResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("Login")] + public async Task> Login ( + [FromBody] XLoginRequest model + ) { + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotNull ( + model, + model.Device) + .AddNotEmpty ( + model.Language, + model.UserSelectBy, + model.Password + ) + .ValidateGroupAsync (); + + // + var result = await identityProvider + .Login (model); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + return result; + } + } + + /// + /// Logout User + /// + [RequireXPowered] + [HttpPost ("Logout")] + [Authorize (Policy = XPolicies.User)] + public async Task Logout () { + try { + // + var tokens = await RetrieveTokensAsXTokenResponse (); + await identityProvider + .Logout (tokens); + + // + return Ok (); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + return result; + } + } + + /// + /// Refresh Expired Tokens + /// + /// + /// in addition to AccessToken, you had to pass RefreshToken due to Headers + /// + /// an instance of XTokenResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("RefreshTokens")] + public async Task> RefreshTokens () { + // + try { + // + // Retrieve Toke Response ... + var model = await RetrieveTokensAsXTokenResponse (); + + // + // Validate Args ... + ValidationProvider + .GroupValidationBuilder () + .AddNotNull (model) + .AddNotEmpty ( + model.AccessToken, + model.RefreshToken) + .ValidateGroup (); + + // + var result = await identityProvider + .RefreshTokens (model); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + return result; + } + } + + /// + /// Validate a Revision Checksum + /// + /// + /// + [HttpPost ("Validate")] + [Authorize (Policy = XPolicies.EnabledUser)] + public async Task> Validate ( + [FromBody] XValidateRevisionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder () + .AddNotNull (model) + .AddNotEmpty (model.Revision) + .ValidateGroupAsync (); + + // + // Get Request ... + var tokens = await RetrieveTokensAsXTokenResponse (); + var result = tokens.ValidateRevisionChecksum ( + model.Revision, + identityProvider.RevisionSecretKey + ); + + // + // Return Result + return Ok (result); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + return result; + } + } + #endregion + + // + #region Password Actions ... + /// + /// Change a User's Password ... + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - Password: user's current active password. + /// - NewPassword: user's new password to change. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Login URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "password": "" + /// "newPassword": "", + /// "returnUrl": "http://localhost/login", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// + [RequireXPowered] + [HttpPost ("ChangePassword")] + [Authorize (Policy = XPolicies.User)] + [Authorize (Policy = XPolicies.EnabledUser)] + public async Task ChangePassword ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotNull ( + model, + model.Device + ) + .AddNotEmpty ( + model.Lang, + model.Password, + model.NewPassword, + model.ReturnUrl + ) + .ValidateGroupAsync (); + + // + // Get User Select By ... + var userSelectByParam = GetUserSelectByParam ( + model, + forceNotNull : false + ); + if (userSelectByParam.IsNullOrEmpty ()) { + userSelectByParam = User.Identity.Name; + } + ValidationProvider.NotEmpty (userSelectByParam); + + // + // Get Request ... + var tokens = await RetrieveTokensAsXTokenResponse (); + await identityProvider + .ChangePassword (tokens, model); + + // + return Ok (); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\Account\AccountController+Friendship.cs + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xIdentityHelper; +using xCommons.Attributes; +using xCommons.Extensions; +using xIdentityModels.Constants; +using xIdentityModels.Dtos; +using xIdentityModels.Navigations; +using xModels.Dtos; +using System.Linq; +using xExceptions.Constants; + +namespace xApi.Controllers +{ + public partial class AccountController + { + // + #region Friendship Actions ... + /// + /// Follow a User + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + [RequireXPowered] + [HttpPost("Friendship/{destUser}/Follow")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> Follow( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .Follow( + tokens, + destUser + ); + + // + // Prepare Push Message ... + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Cancel Following Request + /// + /// a user identifier which represent destination user + /// a boolean value + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpPost("Friendship/{destUser}/Cancel")] + public async Task> Cancel( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .Cancel( + tokens, + destUser + ); + + // + // Prepare Push Message ... + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Unfollow a Follower + /// + /// a user identifier which represent destination user + /// + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpPost("Friendship/{destUser}/UnFollowFollower")] + public async Task UnFollowFollower( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + await identityProvider + .UnFollowFollower( + tokens, + destUser + ); + + // + // Prepare Push Message ... + + // + return Ok(); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Unfollow Following + /// + /// a user identifier which represent destination user + /// + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpPost("Friendship/{destUser}/UnFollowFollowing")] + public async Task UnFollowFollowing( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + await identityProvider + .UnFollowFollowing( + tokens, + destUser + ); + + // + // Prepare Push Message ... + + // + return Ok(); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Block a Follower + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + [HttpPost("Friendship/{destUser}/Block")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> Block( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .Block( + tokens, + destUser + ); + + // + // Prepare Push Message ... + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + + // + return exResult; + } + } + + /// + /// Unblock a Blocked User + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + [HttpPost("Friendship/{destUser}/UnBlock")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> UnBlock( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .UnBlock( + tokens, + destUser + ); + + // + // Prepare Push Message ... + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Accept a Following Request + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollower + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpPost("Friendship/{destUser}/AcceptRequest")] + public async Task> AcceptRequest( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .AcceptRequest( + tokens, + destUser + ); + + // + // Prepare Push Message ... + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Reject a Following Request + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollower + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpPost("Friendship/{destUser}/RejectRequest")] + public async Task> RejectRequest( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .RejectRequest( + tokens, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + #endregion + + // + #region Friendship Getters ... + + /// + /// Get Friendship Info Model between Specific User and Current User + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipInfoDto + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/FriendshipInfo")] + public async Task> FriendshipInfo( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetFriendshipInfo( + tokens, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Check a User IsFollower of Requested User + /// + /// a user identifier which represent destination user + /// a boolean value + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/IsFollower")] + public async Task> IsFollower( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .IsFollower( + tokens, + destUser + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Follower State of a User + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipState + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/FollowerState")] + public async Task> FollowerState( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetFollowerState( + tokens, + destUser + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Specific Follower + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollower + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/Follower")] + public async Task> Follower( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetFollower( + tokens, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Followers List Of Current User + /// + /// a collection of XFriendshipFollower + [HttpGet("Friendship/Followers")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> Followers() + { + // + // Do Action ... + try + { + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetFollowers(tokens); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + + // + return exResult; + } + } + + /// + /// Get All Followers List Includes Blocked, Requested and etc + /// of Current User + /// + /// a collection of XFriendshipFollower + [HttpGet("Friendship/AllFollowers")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> AllFollowers() + { + // + // Do Action ... + try + { + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetAllFollowers(tokens); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get All Followers List Includes Blocked, Requested and etc + /// of Current User based On Query Model ... + /// + /// a Query Result of XFriendDto + [HttpGet("Friendship/QueryFollowers")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> QueryFollowers( + [FromQuery] XQuery query, + [FromQuery] string destUser + ) + { + // + // Do Action ... + try + { + // + // Normalizing Dest User ... + var userInfo = await GetUserInfo(); + bool isAdmin = userInfo.Roles.Contains(XUserRoles.ADMIN); + if (destUser.IsNullOrEmpty()) + { + destUser = userInfo.UserName; + } + bool isDestUserValid = isAdmin || destUser == userInfo.UserId || destUser == userInfo.Email || destUser == userInfo.PhoneNumber || destUser == userInfo.UserName; + if (!isDestUserValid) + { + XException.NotAllowed.Throw(); + } + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .QueryFollowers( + query: query, + tokens: tokens, + destUser: destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Check a User is in Followings of Current User + /// + /// a user identifier which represent destination user + /// a boolean value + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/IsFollowing")] + public async Task> IsFollowing( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .IsFollowing( + tokens, + destUser + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Following State Relation between Specific User + /// and Current User + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipState + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/FollowingState")] + public async Task> FollowingState( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetFollowingState( + tokens, + destUser + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Specific Following Model + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/Following")] + public async Task> Following( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetFollowing( + tokens, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Followings of Current User + /// + /// a collection of XFriendshipFollowing + [HttpGet("Friendship/Followings")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> Followings() + { + // + // Do Action ... + try + { + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetFollowings(tokens); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get All Following List Includes Blocked, Requested and etc + /// of Current User + /// + /// a collection of XFriendshipFollowing + [HttpGet("Friendship/AllFollowings")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> AllFollowings() + { + // + // Do Action ... + try + { + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetAllFollowings(tokens); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Return Following UserName's List of Current User + /// + /// a collection of UserNames + [HttpGet("Friendship/FollowingsList")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> FollowingsList() + { + // + // Do Action ... + try + { + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetFollowingList(tokens); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Return Followers UserName's List of Current User + /// + /// a collection of UserNames + [HttpGet("Friendship/FollowersList")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> FollowersList() + { + // + // Do Action ... + try + { + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetFollowersList(tokens); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get All Followings List Includes Blocked, Requested and etc + /// of Current User based On Query Model ... + /// + /// a Query Result of XFriendDto + [HttpGet("Friendship/QueryFollowings")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> QueryFollowings( + [FromQuery] XQuery query, + [FromQuery] string destUser + ) + { + // + // Do Action ... + try + { + // + // Normalizing Dest User ... + var userInfo = await GetUserInfo(); + bool isAdmin = userInfo.Roles.Contains(XUserRoles.ADMIN); + if (destUser.IsNullOrEmpty()) + { + destUser = userInfo.UserName; + } + bool isDestUserValid = isAdmin || destUser == userInfo.UserId || destUser == userInfo.Email || destUser == userInfo.PhoneNumber || destUser == userInfo.UserName; + if (!isDestUserValid) + { + XException.NotAllowed.Throw(); + } + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .QueryFollowings( + query: query, + tokens: tokens, + destUser: destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\Account\AccountController+Open.cs + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xCommons.Attributes; +using xCommons.Extensions; +using xModels.Dtos; + +namespace xApi.Controllers +{ + public partial class AccountController + { + #region Open Actions ... + [AllowAnonymous] + [RequireXPowered] + [HttpGet("GetUserInfo")] + public async Task> GetUserInfo( + [FromHeader] string token, + [FromQuery] string request + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(token) + .AddNotEmpty(request) + .ValidateGroupAsync(); + + // + var result = await identityProvider.GetUserInfo( + token: token, + request: request + ); + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + [AllowAnonymous] + [RequireXPowered] + [HttpGet("GetUserInfoByDevice")] + public async Task> GetUserInfoByDevice( + [FromQuery] string userSelectByParam, + [FromBody] XDeviceDto device + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(userSelectByParam) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + var result = await identityProvider.GetUserInfo( + userSelectByParam: userSelectByParam, + device: device + ); + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\Account\AccountController+Profile.cs + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using xIdentityHelper; +using xCommons.Attributes; +using xCommons.Extensions; +using xIdentityModels.Dtos; +using xIdentityModels.Models; +using xModels.Dtos; + +namespace xApi.Controllers +{ + public partial class AccountController + { + // + #region Retrieve Actions ... + /// + /// Retrieve User Names based on UserIds + /// + /// a comma seperated list of UserIds + /// a collection of UserNames + [RequireXPowered] + [HttpGet("Profile/{ids}/GetNames")] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> GetNames( + [FromRoute] string ids + ) + { + // + // Do Action ... + try + { + // + ValidationProvider.NotEmpty(ids); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider.GetUserNames( + tokens, + ids + ); + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// get user ids and retrieve corresponding user names + /// + /// an instance of XUserNameIdRequest which represent required UserIds collection + /// a collection of XUserNameIdResponse instance + [RequireXPowered] + [HttpPost("Profile/GetNameIds")] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> GetNameIds( + [FromBody] XUserNameIdRequest model + ) + { + // + // Do Action ... + try + { + // + ValidationProvider.NotNull(model); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider.GetUserNameIds( + tokens, + model + ); + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get Profile Object of Specific User + /// + /// determine's which user profile must be retrieved + /// an instance of XUserProfileDto + [RequireXPowered] + [Authorize(Policy = XPolicies.User)] + [HttpGet("Profile/{userSelectByParam?}")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> GetProfile( + [FromRoute] string userSelectByParam = "" + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (userSelectByParam.IsNullOrEmpty()) + { + userSelectByParam = User.Identity.Name; + } + ValidationProvider.NotEmpty(userSelectByParam); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .GetUserProfile( + tokens, + userSelectByParam + ); + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Query Users + /// for specifiy users role for query user 'role: string' and 'forceRole: boolean' in header + /// + /// how to filter results based on XQuery structure + /// an string which represent user role + /// if it's true the user must has exact role, otherwise top level users also listed + /// an instance of XQueryResult of XUserProfileDto + [RequireXPowered] + [HttpGet("Profile/Query")] + [Authorize(Policy = XPolicies.EnabledAdmin)] + [Authorize(Policy = XPolicies.EnabledAgent)] + public async Task>> QueryProfiles( + [FromQuery] XQuery query, [FromHeader] string role, [FromHeader] bool forceRole = false + ) + { + // + // Do Action ... + try + { + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = new XQueryResult(); + if (role.IsNullOrEmpty()) + { + // + result = await identityProvider.QueryUsers( + tokens, + query + ); + } + else + { + // + result = await identityProvider.QueryInRoleUsers( + tokens, + role, + query, + forceRole + ); + } + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exception = GetExceptionActionResult(ex); + return exception; + } + } + + /// + /// Query Specified User's Profile Images + /// + /// determine's which user profile must be retrieved + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XProfileImage + [RequireXPowered] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = XPolicies.EnabledUser)] + [Authorize(Policy = XPolicies.EnabledAdmin)] + [HttpGet("Profile/Avatars/Query/{userSelectByParam?}")] + public async Task>> QueryAvatars( + [FromQuery] XQuery query, [FromRoute] string userSelectByParam = null + ) + { + // + // Do Action ... + try + { + // + if (userSelectByParam.IsNullOrEmpty()) + { + userSelectByParam = User.Identity.Name; + } + ValidationProvider.NotEmpty(userSelectByParam); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider.QueryAvatars( + tokens, + userSelectByParam, + query + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exception = GetExceptionActionResult(ex); + return exception; + } + } + #endregion + + // + #region Update Actions ... + /// + /// Update User Profile based on XProfileUpdateRequest (FirstName/LastName/DateOfBirth) + /// + /// user update info, an instance of XProfileUpdateRequest + /// determine's which user profile must be retrieved + /// an instance of XUserProfileDto + [RequireXPowered] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpPost("Profile/Update/{userSelectByParam?}")] + public async Task> UpdateProfile( + [FromBody] XProfileUpdateRequest model, [FromRoute] string userSelectByParam = null + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (userSelectByParam.IsNullOrEmpty()) + { + userSelectByParam = User.Identity.Name; + } + ValidationProvider.NotEmpty(userSelectByParam); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider.ProfileUpdateAsync( + tokens, + userSelectByParam, + model + ); + + // + // Prepare Push Message ... + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update a User Profile based on XProfileUpdateRequest Full + /// + /// user update info, an instance of XProfileUpdateRequest + /// determine's which user profile must be retrieved + /// an instance of XUserProfileDto + [RequireXPowered] + [HttpPost("Profile/{userSelectByParam?}")] + [Authorize(Policy = XPolicies.EnabledAdminOrAgent)] + public async Task> FullUpdateProfile( + [FromBody] XProfileUpdateRequest model, [FromRoute] string userSelectByParam = null + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (userSelectByParam.IsNullOrEmpty()) + { + userSelectByParam = User.Identity.Name; + } + ValidationProvider.NotEmpty(userSelectByParam); + + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider.FullProfileUpdateAsync( + tokens, + userSelectByParam, + model + ); + + // + // Prepare Push Message ... + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Avatar Actions ... + /// + /// Add a New Profile Image + /// + /// an specific File to upload, IFormFile + /// an instance of XUserProfileDto + [RequireXPowered] + [HttpPost("Profile/Avatar")] + [RequestSizeLimit(966_367_641)] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> AddAvatar( + [FromForm] IFormFile file + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotNull(file); + + // + // Get Request ... + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .AddAvatar( + tokens, + file + ); + + // + // Prepare Push Message ... + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Add a New Collection of Profile Images + /// + /// a collection of Files to upload, IFormFileCollection + /// an instance of XUserProfileDto + [RequireXPowered] + [HttpPost("Profile/Avatars")] + [RequestSizeLimit(966_367_641)] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> AddAvatars( + [FromForm] IFormFileCollection files + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotZeroChilds(files); + + // + // Get Request ... + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider.AddAvatars( + tokens, + files + ); + + // + // Prepare Push Message ... + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// set Specified Profile Image as Avatar + /// + /// an integer which reperesent AvatarId to set as current Avatar + /// an instance of XUserProfileDto + [RequireXPowered] + [HttpPost("Profile/Avatars/{id:int}/Set")] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> SetAvatar( + [FromRoute] int id + ) + { + // + // Do Action ... + try + { + // + // Get Request ... + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .SetAvatar( + tokens, + id + ); + + // + // Prepare Push Message ... + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exception = GetExceptionActionResult(ex); + return exception; + } + } + + /// + /// Remove Profile Images + /// + /// a comma seperated list of avatarIds to remove + /// an instance of XUserProfileDto + [RequireXPowered] + [Authorize(Policy = XPolicies.User)] + [HttpDelete("Profile/Avatars/{ids}")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> RemoveAvatars( + [FromRoute] string ids + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(ids); + + // + // Get Request ... + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider + .RemoveAvatars( + tokens, + ids + ); + + // + // Prepare Push Message ... + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\Account\AccountController+Registration.cs + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using xIdentityHelper; +using xCommons.Attributes; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; + +namespace xApi.Controllers { + public partial class AccountController { + // + #region Registration ... + /// + /// Check a UserName is Available For Registration or not + /// + /// an string value which represent desired username + /// a boolean value + [AllowAnonymous] + [RequireXPowered] + [HttpGet ("CanRegisterUserName/{userName?}")] + public async Task> CanRegisterUserName ( + [FromRoute] string userName + ) { + // + // Do Action ... + try { + // + // Validate Args ... + ValidationProvider.NotEmpty (userName); + + // + // Validate Type ... + var type = userName.GetUserSelectByType (); + if (type != XUserSelectBy.Username) { + XException.InvalidArgs.Throw (); + } + + // + var result = await identityProvider + .CanRegister (userName); + + // + // Return Result + return Ok (result); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + + // + return result; + } + } + + /// + /// Check a Mobile Number is Available For Registration or not + /// + /// an string value which represent desired mobile number + /// a boolean value + [AllowAnonymous] + [RequireXPowered] + [HttpGet ("CanRegisterMobileNumber/{mobileNumber?}")] + public async Task> CanRegisterMobileNumber ( + [FromRoute] string mobileNumber + ) { + // + // Do Action ... + try { + // + // Validate Args ... + ValidationProvider.NotEmpty (mobileNumber); + + // + // Validate Type ... + var type = mobileNumber.GetUserSelectByType (); + if (type != XUserSelectBy.MobileNumber) { + XException.InvalidArgs.Throw (); + } + + // + var result = await identityProvider + .CanRegister (mobileNumber); + + // + // Return Result + return Ok (result); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + + // + return result; + } + } + + /// + /// Check a Email Address is Available For Registration or not + /// + /// an string value which represent desired email address + /// a boolean value + [AllowAnonymous] + [RequireXPowered] + [HttpGet ("CanRegisterEmail/{email?}")] + public async Task> CanRegisterEmail ( + [FromRoute] string email + ) { + // + // Do Action ... + try { + // + // Validate Args ... + ValidationProvider.NotEmpty (email); + + // + // Validate Type ... + var type = email.GetUserSelectByType (); + if (type != XUserSelectBy.Email) { + XException.InvalidArgs.Throw (); + } + + // + var result = await identityProvider + .CanRegister (email); + + // + // Return Result + return Ok (result); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + + // + return result; + } + } + + /// + /// Invite a User to Register on Dashboard + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - Email: which email address is going to invite. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Registration URL. + /// notice in this step you had to provide all this informations as FormData + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "email": "", + /// "returnUrl": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [RequireXPowered] + [HttpPost ("InviteUser")] + [Authorize (Policy = XPolicies.EnabledAdmin)] + [Authorize (Policy = XPolicies.EnabledAgent)] + public async Task> InviteUser ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + + // + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.Email, + model.ReturnUrl) + .AddEmailAddress (model.Email) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + // Get Request ... + var tokens = await RetrieveTokensAsXTokenResponse (); + var result = await identityProvider + .InviteUser ( + tokens, + model + ); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + return result; + } + } + + /// + /// Recieve some Basic Informations and Start Registration Proccess + /// if they Valid + /// + /// Registration Proccess Starts with Invoking this Action + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - FirstName: user's FirstName. + /// - LastName: user's LastName. + /// - DateOfBirth: user's dob date. + /// - MobileNumber: user's MobileNumber optional. + /// - Email: user's Email optional. + /// - ActionToken: user's requested action token for this action (if user invited). + /// - Device: user's client device which is an instance of XDevice. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "firstName": "", + /// "lastName": "", + /// "dateOfBirth": "", + /// "email": "", + /// "mobileNumber": "", + /// "actionToken": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("RequestRegistration")] + public async Task> RequestRegistration ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.FirstName, + model.LastName) + .AddNotNull ( + model.Device, + model.DateOfBirth) + .ValidateGroupAsync (); + + // + // Get Request ... + var result = await identityProvider + .RequestRegistration ( + model + ); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + + // + return exResult; + } + } + + /// + /// Add User Account Informations + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - UserName: user name. + /// - Password: assigne password. + /// - Device: user's client device which is an instance of XDevice. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "userName": "", + /// "password": "", + /// "actionToken": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("AddAccountInfo")] + public async Task> AddAccountInfo ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.ActionToken, + model.UserName, + model.Password) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + // Get Request ... + var result = await identityProvider + .AddAccountInfo ( + model + ); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// Upload and Attach a Profile Image for User. this is an Optional Step + /// + /// user's requested action token + /// an instance of IFormFile for user's Avatar + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("AttachProfileImage")] + public async Task> AttachProfileImage ( + [FromForm] string actionToken, [FromForm] IFormFile file + ) { + // + // Do Action ... + try { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty (actionToken) + .AddNotNull (file) + .ValidateGroupAsync (); + + // + var result = await identityProvider + .AttachProfileImage ( + actionToken, + file + ); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// Finish Registrationn Proccess + /// + /// + /// for this action you have to provide: + /// - a proper User Identifier such as: (UserId, UserName, Email or MobileNumber). + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - Password: assigne password. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Login URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "userName": "", + /// "password": "", + /// "returnUrl": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("FinishRegistration")] + public async Task FinishRegistration ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.Password, + model.ReturnUrl) + .AddNotNull (model.Device) + .AddUrl (model.ReturnUrl) + .ValidateGroupAsync (); + + // + var userSelectByParam = identityProvider + .GetUserSelectByParam (model); + ValidationProvider + .NotEmpty (userSelectByParam); + + // + // Get Request ... + await identityProvider + .FinishRegistration (model); + + // + return Ok (); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + + // + return exResult; + } + } + + /// + /// Check User is Confirmed Email or not + /// + /// a boolean value + [RequireXPowered] + [HttpGet ("IsConfirmedEmail")] + [Authorize (Policy = XPolicies.User)] + public async Task> IsConfirmedEmail () { + // + // Do Action ... + try { + // + // Get Result ... + var tokens = await RetrieveTokensAsXTokenResponse (); + var result = await identityProvider + .IsConfirmedEmail ( + tokens, + User.Identity.Name + ); + + // + // Return Result + return Ok (result); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + + // + return result; + } + } + + /// + /// Check User is Confirmed Mobile or not + /// + /// a boolean value + [RequireXPowered] + [HttpGet ("IsConfirmedMobile")] + [Authorize (Policy = XPolicies.User)] + public async Task> IsConfirmedMobile () { + // + // Do Action ... + try { + // + // Get Result ... + var tokens = await RetrieveTokensAsXTokenResponse (); + var result = await identityProvider + .IsConfirmedMobile ( + tokens, + User.Identity.Name + ); + + // + // Return Result + return Ok (result); + } catch (Exception ex) { + // + var result = GetExceptionActionResult (ex); + + // + return result; + } + } + + /// + /// Request Registration Confirm + /// + /// + /// for this action you have to provide: + /// - a UserSelectedBy identifier which represet User. + /// - Lang: client device currently used locale, such as: en-US. + /// - Password: user's current active password. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Confirm Registration URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "userId": "" + /// "password": "", + /// "returnUrl": "http://localhost/confirm", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("RequestConfirmRegistration")] + public async Task RequestConfirmRegistration ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.Password, + model.ReturnUrl) + .AddNotNull (model.Device) + .AddUrl (model.ReturnUrl) + .ValidateGroupAsync (); + + // + // Get UserSelect By Param ... + var userSelectByParam = model.GetUserSelectByParam (); + ValidationProvider.NotEmpty (userSelectByParam); + + // + await identityProvider + .RequestConfirmRegistration (model); + + // + return Ok (); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// Confirm Registration + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Login URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "actionToken": "" + /// "returnUrl": "http://localhost/login", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("ConfirmRegistration")] + public async Task ConfirmRegistration ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.ActionToken, + model.ReturnUrl + ) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + // Get Request ... + await identityProvider + .ConfirmRegistration (model); + + // + return Ok (); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// Request For Mobile Number Change/Confirm + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - Password: user's current active password. + /// - MobileNumber: which phone number is going to confirm. + /// - Device: user's client device which is an instance of XDevice. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "password": "", + /// "mobileNumber": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("RequestConfirmMobile")] + public async Task> RequestConfirmMobile ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.Password, + model.MobileNumber) + .AddNotNull (model.Device) + .AddMobileNumber (model.MobileNumber) + .ValidateGroupAsync (); + + // + // Get UserSelect By Param ... + var userSelectByParam = GetUserSelectByParam ( + model, + excludes : new List { + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + + // + // Get Request ... + var result = await identityProvider + .RequestConfirmMobile (model); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// Confirm Requested Mobile Number + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - Device: user's client device which is an instance of XDevice. + /// - MobileVerificationCode: a verification code which produced due Request Action. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "actionToken": "" + /// "mobileVerificationCode": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("ConfirmMobileNumber")] + public async Task> ConfirmMobileNumber ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.MobileVerificationCode, + model.ActionToken + ) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + // Get Request ... + var result = await identityProvider + .ConfirmMobileNumber (model); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// Request For Email Address Change/Confirm + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - Password: user's current active password. + /// - Email: which email address is going to confirm. + /// - Device: user's client device which is an instance of XDevice. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "password": "", + /// "email": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("RequestConfirmEmail")] + public async Task> RequestConfirmEmail ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.Email, + model.Password) + .AddNotNull (model.Device) + .AddEmailAddress (model.Email) + .ValidateGroupAsync (); + + // + // Get UserSelect By Param ... + var userSelectByParam = GetUserSelectByParam ( + model, + excludes : new List { + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + + // + // Get Request ... + var result = await identityProvider + .RequestConfirmEmail (model); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// Confirm Requested Email Address + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - Device: user's client device which is an instance of XDevice. + /// - EmailVerificationCode: a verification code which produced due Request Action. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "actionToken": "" + /// "emailVerificationCode": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("ConfirmEmailAddress")] + public async Task> ConfirmEmailAddress ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.EmailVerificationCode, + model.ActionToken + ) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + // Get Request ... + var result = await identityProvider + .ConfirmEmailAddress (model); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// Request Reset Password Action + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application ResetPassword URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "returnUrl": "http://localhost/reset-password", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("RequestResetPassword")] + public async Task> RequestResetPassword ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.ReturnUrl) + .AddNotNull (model.Device) + .AddUrl (model.ReturnUrl) + .ValidateGroupAsync (); + + // + // Get UserSelect By Param ... + var userSelectByParam = GetUserSelectByParam ( + model, + excludes : new List { + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + + // + // Get Request ... + var result = await identityProvider + .RequestResetPassword (model); + + // + return Ok (result + .ToDynamicObject ()); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + + /// + /// Reset a User's Password + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - NewPassword: user's new password to change. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Login URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "actionToken": "", + /// "newPassword": "", + /// "returnUrl": "http://localhost/login", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// + [AllowAnonymous] + [RequireXPowered] + [HttpPost ("ResetPassword")] + public async Task ResetPassword ( + [FromBody] XActionRequest model + ) { + // + // Do Action ... + try { + // + // Validate Args ... + if (!ModelState.IsValid) { + XException.InvalidArgs.Throw (); + } + await ValidationProvider + .GroupValidationBuilder () + .AddNotEmpty ( + model.Lang, + model.ActionToken, + model.NewPassword, + model.ReturnUrl) + .AddNotNull (model.Device) + .ValidateGroupAsync (); + + // + // Get Request ... + await identityProvider + .ResetPassword (model); + + // + return Ok (); + } catch (Exception ex) { + // + var exResult = GetExceptionActionResult (ex); + return exResult; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\Account\AccountController+Search.cs + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xCommons.Attributes; +using xCommons.Extensions; +using xIdentityHelper; +using xIdentityModels.Dtos; +using xModels.Dtos; + +namespace xApi.Controllers +{ + public partial class AccountController + { + // + #region Actions ... + /// + /// Query Open To Search Users Profiles ... + /// Query all Users Which their Profiles is Open To Search ... + /// + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XUserProfileDto + [RequireXPowered] + [HttpGet("OpenToSearch/Query")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> QueryOpenToSearchProfiles( + [FromQuery] XQuery query + ) + { + // + // Do Action ... + try + { + // + var tokens = await RetrieveTokensAsXTokenResponse(); + var result = await identityProvider.QueryOpenToSearchUsers( + tokens, + query + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exception = GetExceptionActionResult(ex); + return exception; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\AccountController.cs + +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Logging; +using xCommons.Attributes; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; + +namespace xApi.Controllers +{ + /// + /// Handle all Account Related Actions ... + /// + [AllowAnonymous] + [RequireXPowered(true)] + public partial class AccountController : XBaseIdentityController + { + // + #region Props ... + private readonly IXIdentityProvider identityProvider; + #endregion + + // + #region Constructor ... + public AccountController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + identityProvider, + validationProvider + ) + { + // + this.identityProvider = identityProvider; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\StartupController.cs + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Controllers; +using xCommons.Providers; + +namespace xApi.Controllers { + /// + /// Runing when application start ... + /// + [Route ("")] + [AllowAnonymous] + public class StartupController : XBaseController { + + public StartupController ( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base ( + logger, + appConfiguration, + validationProvider + ) { } + + /// + /// Show Configured Welcome Message + /// + /// + [HttpGet ("")] + [AllowAnonymous] + public virtual ActionResult Index () { + // + var controllerName = GetControllerName (); + var message = $"{AppConfiguration.WelcomeMessage}"; + + // + return Ok (message); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\TestFeaturesController.cs + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCategoryService.Interfaces; +using xCommons.Configurations; +using xCommons.Controllers; +using xCommons.Providers; + +namespace xApi.Controllers +{ + [AllowAnonymous] + public class TestFeaturesController : XBaseApiController + { + private readonly IXSubCategoryTitleResourceProvider subCategoryTitleResourceProvider; + + public TestFeaturesController( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXSubCategoryTitleResourceProvider subCategoryTitleResourceProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { + this.subCategoryTitleResourceProvider = subCategoryTitleResourceProvider; + } + + [HttpGet] + public async Task TestFeature() + { + // + var result = await subCategoryTitleResourceProvider.GetLanguages( + identifier: "SSSSAAALLLAAAM" + ); + + // + return Ok(result); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Controllers\TestIdentity.cs + +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Attributes; +using xCommons.Configurations; +using xCommons.Providers; +using xIdentityHelper; +using xIdentityService.Controllers; +using xIdentityService.Interfaces; + +namespace xApi.Controllers { + /// + /// Test all Authentication Policies ... + /// + [RequireXPowered (false)] + public class TestIdentity : XBaseIdentityController { + public TestIdentity ( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityProvider identityProvider, + XValidationProvider validationProvider + ) : base ( + logger, + appConfiguration, + identityProvider, + validationProvider + ) { } + + // + #region Test Actions ... + /// + /// API Read Scope + /// + /// string message + [HttpGet ("PassReadAccess")] + [Authorize (Policy = XPolicies.ReadAccess)] + public ActionResult PassReadAccess () { + return Ok ("Read Access Passed ..."); + } + + /// + /// API Write Scope + /// + /// string message + [HttpGet ("PassWriteAccess")] + [Authorize (Policy = XPolicies.WriteAccess)] + public ActionResult PassWriteAccess () { + return Ok ("Write Access Passed ..."); + } + + /// + /// API Admin Scope + /// + /// string message + [HttpGet ("PassAdminAccess")] + [Authorize (Policy = XPolicies.AdminAccess)] + public ActionResult PassAdminAccess () { + return Ok ("Admin Access Passed ..."); + } + + /// + /// API Manage Scop + /// + /// string message + [HttpGet ("PassManageAccess")] + [Authorize (Policy = XPolicies.ManageAccess)] + public ActionResult PassManageAccess () { + return Ok ("Manage Access Passed ..."); + } + + /// + /// a simple Action which returns a List of Authenticated User Claims + /// + /// string message which represent current user's claims + [HttpGet ("HiClaims")] + [Authorize (Policy = XPolicies.User)] + public ActionResult HiClaims () { + // + var result = new { + name = User.Identity.Name, + claims = User.Claims.Select (c => new { + c.Type, c.Value + }) + }; + + // + return Ok (result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet ("HiUser")] + [Authorize (Policy = XPolicies.User)] + public ActionResult HiUser () { + // + var result = $"Hi User: {User.Identity.Name} ..."; + + // + return Ok (result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet ("HiEnabledUser")] + [Authorize (Policy = XPolicies.EnabledUser)] + public ActionResult HiEnabledUser () { + // + var result = $"Hi User: {User.Identity.Name} is Enabled ..."; + + // + return Ok (result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet ("HiAgent")] + [Authorize (Policy = XPolicies.Agent)] + public ActionResult HiAgent () { + // + var result = $"Hi Agent: {User.Identity.Name} ..."; + + // + return Ok (result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet ("HiEnabledAgent")] + [Authorize (Policy = XPolicies.EnabledAgent)] + public ActionResult HiEnabledAgent () { + // + var result = $"Hi Agent: {User.Identity.Name} is Enabled ..."; + + // + return Ok (result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet ("HiAdmin")] + [Authorize (Policy = XPolicies.Admin)] + public ActionResult HiAdmin () { + // + var result = $"Hi Admin: {User.Identity.Name} ..."; + + // + return Ok (result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [HttpGet ("HiEnabledAdmin")] + [Authorize (Policy = XPolicies.EnabledAdmin)] + public ActionResult HiEnabledAdmin () { + // + var result = $"Hi Admin: {User.Identity.Name} is Enabled ..."; + + // + return Ok (result); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Extensions\XStartupExtensions.cs + +using System.Collections.Generic; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.Extensions.DependencyInjection; +using xIdentityHelper; +using xCommons.Authorization; +using xCommons.Extensions; + +namespace xApi.Extensions { + public static class XStartupExtensions { + /// + /// Register Authorization Policies + /// + /// + /// + public static void AddXAuthorization ( + this IServiceCollection services, + IDictionary policies = null + ) { + // + services.AddAuthorization (options => { + // + if (!policies.IsNull ()) { + // + // Fill Policies ... + using (var policiesEnumerator = policies.GetEnumerator ()) { + while (policiesEnumerator.MoveNext ()) { + var policyDescriptor = policiesEnumerator.Current; + options.AddPolicy (policyDescriptor.Key, policyDescriptor.Value); + } + } + } + + // + var xPolicies = XAuthorizationHelper.GetXAuthorizationPolicies (); + if (!xPolicies.IsNull ()) { + // + // Fill Policies ... + using (var policiesEnumerator = xPolicies.GetEnumerator ()) { + while (policiesEnumerator.MoveNext ()) { + var policyDescriptor = policiesEnumerator.Current; + options.AddPolicy (policyDescriptor.Key, policyDescriptor.Value); + } + } + } + }); + + // + services.AddSingleton (); + } + + /// + /// Use Forward Headers Options for resolving behind a proxy issues ... + /// + /// + /// + public static void UseXForwardOptions (this IApplicationBuilder source) { + // + var forwardOptions = new ForwardedHeadersOptions { + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto, + RequireHeaderSymmetry = false + }; + + // + forwardOptions.KnownNetworks.Clear (); + forwardOptions.KnownProxies.Clear (); + + // + // ref: https://github.com/aspnet/Docs/issues/2384 + source.UseForwardedHeaders (forwardOptions); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Extensions\XUserClaimsInfoDtoExtensions.cs + +using xCommons.Extensions; +using xIdentityModels.Constants; +using xIdentityModels.Models; + +namespace xApi.Extensions { + public static class XUserClaimsInfoDtoExtensions { + public static string GetUserSelectByParam ( + this XUserClaimsInfoDto source, + XUserSelectBy userSelectBy = XUserSelectBy.Username + ) { + // + var result = ""; + + // + switch (userSelectBy) { + // + case XUserSelectBy.ID: + result = source.UserId; + break; + + // + case XUserSelectBy.Username: + result = source.UserName; + break; + + // + case XUserSelectBy.Email: + result = source.Email; + break; + + // + case XUserSelectBy.MobileNumber: + result = source.PhoneNumber; + break; + } + + // + if (result.IsNullOrEmpty ()) { + // + if (!source.UserId.IsNullOrEmpty ()) { + result = source.UserId; + } else if (!source.UserName.IsNullOrEmpty ()) { + result = source.UserName; + } else if (!source.Email.IsNullOrEmpty ()) { + result = source.Email; + } else if (!source.PhoneNumber.IsNullOrEmpty ()) { + result = source.PhoneNumber; + } + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Program.cs + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace xApi +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xApi\Startup.cs + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Reflection; +using IdentityModel.AspNetCore.OAuth2Introspection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using xApi.Extensions; +using xCommons.Configurations; +using xCommons.Extensions; +using xDataService.Models; +using xHttpService.DI; +using xIdentityService.DI; +using xServices.Databases; +using xServices.DI; + +namespace xApi +{ + public class Startup + { + // + public IConfiguration Configuration { get; } + private readonly XApiDatabaseDescriptor apiDatabaseDescriptor; + + public Startup(IConfiguration configuration) + { + // + Configuration = configuration; + + // + // Create an Api Descriptor Class Instance ... + apiDatabaseDescriptor = new XApiDatabaseDescriptor(); + } + + public void ConfigureServices(IServiceCollection services) + { + // + // Register Validation Provider and all XCommons Module Services ... + services.AddXCommons(); + + // + // Register App Configuration ... + services.AddXAppConfiguration(Configuration); + var appConfiguration = services.GetRegisteredService(); + + // + // Register Allowed Origins ... + services.AddXCors(appConfiguration.AllowedOrigins); + + // + // Register Swagger ... + var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + services.AddXSwagger(Configuration, xmlFilePath: xmlPath); + + // + // Register HttpService ... + services.AddXHttpService(Configuration); + + // + // Add Api V1 Versioning ... + services.AddXApiV1Versioning(); + + // + // OAuthIntrospectin Http Client Handler ... + // this is for handling SSLErrors ... + services.AddHttpClient(OAuth2IntrospectionDefaults.BackChannelHttpClientName) + .ConfigurePrimaryHttpMessageHandler(() => + { + return new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true + }; + }); + + // + // Register Authorizations ... + services.AddXAuthorization(); + + // + services.AddControllers() + .AddNewtonsoftJson(x => + { + x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + x.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + }); + + // + // Custom ... + + // + services.Configure(options => + { + options.AllowSynchronousIO = true; + }); + + // + services.Configure(options => + { + options.AllowSynchronousIO = true; + }); + + // + // Register Services ... + services.AddXServices( + configuration: Configuration, + databases: new List + { + apiDatabaseDescriptor + }, + lifeTime: ServiceLifetime.Scoped + ); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + // + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + // + // Use Swagger Middleware ... + app.UseXSwagger(); + + // + app.UseHttpsRedirection(); + + // + // Using Cors ... + app.UseXCors(); + + // + app.UseRouting(); + + // + // Use xIdentityService ... + app.UseXIdentityService(); + + // + app.UseAuthorization(); + + // + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + + // + // Use xDataService Middleware ... + app.UseXServices( + databases: new List + { + apiDatabaseDescriptor + }, + isDevelopmentEnvironment: env.IsDevelopment() + ); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\plugins\cordova-plugin-splashscreen\src\wp\ResolutionHelper.cs + +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using Microsoft.Phone.Info; +using System; +using System.Windows; + +namespace WPCordovaClassLib.Cordova.Commands +{ + public enum Resolutions { WVGA, WXGA, HD }; + + public static class ResolutionHelper + { + public static Resolutions CurrentResolution + { + get + { + switch (Application.Current.Host.Content.ScaleFactor) + { + case 100: return Resolutions.WVGA; + case 160: return Resolutions.WXGA; + case 150: return Resolutions.HD; + } + throw new InvalidOperationException("Unknown resolution"); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\plugins\cordova-plugin-splashscreen\src\wp\SplashScreen.cs + +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Info; +using System.Windows.Controls.Primitives; +using System.Diagnostics; +using System.Windows.Media.Imaging; +using System.Windows.Resources; +using System.IO; +using System.Xml.Linq; +using System.Linq; +using System.Windows.Threading; + +namespace WPCordovaClassLib.Cordova.Commands +{ + /// + /// Listens for changes to the state of the battery on the device. + /// Currently only the "isPlugged" parameter available via native APIs. + /// + public class SplashScreen : BaseCommand + { + private Popup popup; + + // Time until we dismiss the splashscreen + private int prefDelay = 3000; + + // Whether we hide it by default + private bool prefAutoHide = true; + + // Path to image to use + private string prefImagePath = "SplashScreenImage.jpg"; + + // static because autodismiss is only ever applied once, at app launch + // subsequent page loads should not cause the SplashScreen to be shown. + private static bool WasShown = false; + + public SplashScreen() + { + LoadConfigPrefs(); + + Image SplashScreen = new Image() + { + Height = Application.Current.Host.Content.ActualHeight, + Width = Application.Current.Host.Content.ActualWidth, + Stretch = Stretch.Fill + }; + + var imageResource = GetSplashScreenImageResource(); + if (imageResource != null) + { + BitmapImage splash_image = new BitmapImage(); + splash_image.SetSource(imageResource.Stream); + SplashScreen.Source = splash_image; + } + + // Instansiate the popup and set the Child property of Popup to SplashScreen + popup = new Popup() { IsOpen = false, + Child = SplashScreen, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Center + + }; + } + + public override void OnInit() + { + // we only want to autoload on the first page load. + // but OnInit is called for every page load. + if (!SplashScreen.WasShown) + { + SplashScreen.WasShown = true; + show(); + } + } + + private void LoadConfigPrefs() + { + StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("config.xml", UriKind.Relative)); + if (streamInfo != null) + { + using (StreamReader sr = new StreamReader(streamInfo.Stream)) + { + //This will Read Keys Collection for the xml file + XDocument configFile = XDocument.Parse(sr.ReadToEnd()); + + string configAutoHide = configFile.Descendants() + .Where(x => x.Name.LocalName == "preference") + .Where(x => (string)x.Attribute("name") == "AutoHideSplashScreen") + .Select(x => (string)x.Attribute("value")) + .FirstOrDefault(); + + bool bVal; + prefAutoHide = bool.TryParse(configAutoHide, out bVal) ? bVal : prefAutoHide; + + string configDelay = configFile.Descendants() + .Where(x => x.Name.LocalName == "preference") + .Where(x => (string)x.Attribute("name") == "SplashScreenDelay") + .Select(x => (string)x.Attribute("value")) + .FirstOrDefault(); + int nVal; + prefDelay = int.TryParse(configDelay, out nVal) ? nVal : prefDelay; + + string configImage = configFile.Descendants() + .Where(x => x.Name.LocalName == "preference") + .Where(x => (string)x.Attribute("name") == "SplashScreen") + .Select(x => (string)x.Attribute("value")) + .FirstOrDefault(); + + if (!String.IsNullOrEmpty(configImage)) + { + prefImagePath = configImage; + } + } + } + } + + private StreamResourceInfo GetSplashScreenImageResource() + { + // Get the base filename for the splash screen images + string imageName = System.IO.Path.GetFileNameWithoutExtension(prefImagePath); + Uri imageUri = null; + StreamResourceInfo imageResource = null; + + // First, try to get a resolution-specific splashscreen + try + { + // Determine the device's resolution + switch (ResolutionHelper.CurrentResolution) + { + case Resolutions.HD: + imageUri = new Uri(imageName + ".screen-720p.jpg", UriKind.Relative); + break; + + case Resolutions.WVGA: + imageUri = new Uri(imageName + ".screen-WVGA.jpg", UriKind.Relative); + break; + + case Resolutions.WXGA: + default: + imageUri = new Uri(imageName + ".screen-WXGA.jpg", UriKind.Relative); + break; + } + + imageResource = Application.GetResourceStream(imageUri); + } + catch (Exception) + { + // It's OK if we didn't get a resolution-specific image + } + + // Fallback to the default image name without decoration + if (imageResource == null) + { + imageUri = new Uri(prefImagePath, UriKind.Relative); + imageResource = Application.GetResourceStream(imageUri); + } + + if (imageUri != null) Debug.WriteLine("INFO :: SplashScreen: using image {0}", imageUri.OriginalString); + + return imageResource; + } + + public void show(string options = null) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + if (!popup.IsOpen) + { + popup.Child.Opacity = 0; + + Storyboard story = new Storyboard(); + DoubleAnimation animation = new DoubleAnimation() + { + From = 0.0, + To = 1.0, + Duration = new Duration(TimeSpan.FromSeconds(0.2)) + }; + + Storyboard.SetTarget(animation, popup.Child); + Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity")); + story.Children.Add(animation); + + story.Begin(); + + popup.IsOpen = true; + + if (prefAutoHide) + { + StartAutoHideTimer(); + } + } + }); + } + + public void hide(string options = null) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + if (popup.IsOpen) + { + popup.Child.Opacity = 1.0; + + Storyboard story = new Storyboard(); + DoubleAnimation animation = new DoubleAnimation() + { + From = 1.0, + To = 0.0, + Duration = new Duration(TimeSpan.FromSeconds(0.4)) + }; + + Storyboard.SetTarget(animation, popup.Child); + Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity")); + story.Children.Add(animation); + story.Completed += (object sender, EventArgs e) => + { + popup.IsOpen = false; + }; + story.Begin(); + } + }); + } + + private void StartAutoHideTimer() + { + var timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(prefDelay) }; + timer.Tick += (object sender, EventArgs e) => + { + hide(); + timer.Stop(); + }; + timer.Start(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\plugins\cordova-plugin-statusbar\src\wp\StatusBar.cs + +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + + +using Microsoft.Phone.Shell; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Threading; +using System.Windows; +using System.Windows.Media; +using System.Windows.Threading; + + +/* + * http://www.idev101.com/code/User_Interface/StatusBar.html + * https://developer.apple.com/library/ios/documentation/userexperience/conceptual/transitionguide/Bars.html + * https://developer.apple.com/library/ios/documentation/uikit/reference/UIApplication_Class/Reference/Reference.html#//apple_ref/c/econst/UIStatusBarStyleDefault + * */ + + +namespace WPCordovaClassLib.Cordova.Commands +{ + public class StatusBar : BaseCommand + { + + // returns an argb value, if the hex is only rgb, it will be full opacity + protected Color ColorFromHex(string hexString) + { + string cleanHex = hexString.Replace("#", "").Replace("0x", ""); + // turn #FFF into #FFFFFF + if (cleanHex.Length == 3) + { + cleanHex = "" + cleanHex[0] + cleanHex[0] + cleanHex[1] + cleanHex[1] + cleanHex[2] + cleanHex[2]; + } + // add an alpha 100% if it is missing + if (cleanHex.Length == 6) + { + cleanHex = "FF" + cleanHex; + } + int argb = Int32.Parse(cleanHex, NumberStyles.HexNumber); + Color clr = Color.FromArgb((byte)((argb & 0xff000000) >> 0x18), + (byte)((argb & 0xff0000) >> 0x10), + (byte)((argb & 0xff00) >> 8), + (byte)(argb & 0xff)); + return clr; + } + + public void _ready(string options) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + bool isVis = SystemTray.IsVisible; + // TODO: pass this to JS + //Debug.WriteLine("Result::" + res); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isVis)); + }); + } + + public void overlaysWebView(string options) + { //exec(null, null, "StatusBar", "overlaysWebView", [doOverlay]); + // string arg = JSON.JsonHelper.Deserialize(options)[0]; + } + + public void styleDefault(string options) + { //exec(null, null, "StatusBar", "styleDefault", []); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.ForegroundColor = Colors.Black; + }); + } + + public void styleLightContent(string options) + { //exec(null, null, "StatusBar", "styleLightContent", []); + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.ForegroundColor = Colors.White; + }); + } + + public void styleBlackTranslucent(string options) + { //exec(null, null, "StatusBar", "styleBlackTranslucent", []); + styleLightContent(options); + } + + public void styleBlackOpaque(string options) + { //exec(null, null, "StatusBar", "styleBlackOpaque", []); + styleLightContent(options); + } + + public void backgroundColorByName(string options) + { //exec(null, null, "StatusBar", "backgroundColorByName", [colorname]); + // this should NOT be called, js should now be using/converting color names to hex + } + + public void backgroundColorByHexString(string options) + { //exec(null, null, "StatusBar", "backgroundColorByHexString", [hexString]); + string argb = JSON.JsonHelper.Deserialize(options)[0]; + + Color clr = ColorFromHex(argb); + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.Opacity = clr.A / 255.0d; + SystemTray.BackgroundColor = clr; + + }); + } + + public void hide(string options) + { //exec(null, null, "StatusBar", "hide", []); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.IsVisible = false; + }); + + } + + public void show(string options) + { //exec(null, null, "StatusBar", "show", []); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.IsVisible = true; + }); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Configurations\CertificatesConfigurations.cs + +namespace xIds.Configurations +{ + public class CertificatesConfigurations + { + public struct CertificateNames + { + public const string IdentityServerSigning = "IdentityServerSigning"; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Configurations\XDataServiceConfiguration.cs + +using xIds.Constants; + +namespace xIds.Configurations +{ + /// + /// Represent Configurations of DataService Module ... + /// + public partial class XDataServiceConfiguration + { + /// + /// Provider Type ... + /// + /// + public XDbProviders Provider { get; set; } + + /// + /// Connection String which provide Requires Data to Connect to Db Provider ... + /// + /// + public string ConnectionString { get; set; } + + /// + /// Enable Tracking of Entities ... + /// Only Used on EFCore ... + /// + /// + public bool EnableTracking { get; set; } = false; + + /// + /// Enable Logging Details of Errors ... + /// Only Used on EFCore ... + /// + /// + public bool EnableDetailedErrors { get; set; } = false; + + /// + /// Enable Logging Sensitive Data ... + /// Only Used on EFCore ... + /// + /// + public bool EnableSensitiveDataLogging { get; set; } = false; + + /// + /// this is a way to provide Default Pagination Data on XQuery based requests ... + /// + /// + public PagingConfiguration PagingConfiguration { get; set; } = new PagingConfiguration(); + } + + /// + /// this is a way to provide Default Pagination Data on XQuery based requests ... + /// + public partial class PagingConfiguration + { + /// + /// Default Page Size ... + /// + /// + public int DefaultPageSize { get; set; } = XDataServiceConstants.DEFAULT_PAGE_SIZE; + + /// + /// restrict Maximum Page Size ... + /// + /// + public int MaxAvailablePageSize { get; set; } = XDataServiceConstants.MAX_AVAILABLE_PAGE_SIZE; + + /// + /// restrice Minimum Page Size ... + /// + /// + public int MinAvailablePageSize { get; set; } = XDataServiceConstants.MIN_AVAILABLE_PAGE_SIZE; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Configurations\XIdentityResourceConfiguration.cs + +namespace xIds.Configurations +{ + /// + /// determines a resource and it's connection info + /// + public partial class XIdentityResourceConfiguration + { + public string Authority { get; set; } + public string ApiName { get; set; } + public string ApiSecret { get; set; } + + public string ClientId { get; set; } + public string ClientSecret { get; set; } + + public string XPoweredValue { get; set; } + public string XRevisionSecretKey { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Constants\ConfigurationNodeNames.cs + +namespace xIds.Constants +{ + public struct ConfigurationNodeNames + { + public const string DB_SEED_NODE_NAME = "DbSeeder"; + public const string PROVIDER_NODE_NAME = "DbProvider"; + public const string IDENTITY_NODE_NAME = "IdentityConfiguration"; + public const string DATA_SERVICE_NODE_NAME = "DataServiceConfiguration"; + public const string IDENTITY_RESOURCE_NODE_NAME = "IdentityResourceConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Constants\ConnectionStringNames.cs + +namespace xIds.Constants +{ + public struct ConnectionStringNames + { + public const string IDENTITY_CONNECTION_NAME = "IdentityDb"; + public const string CONFIGURATION_CONNECTION_NAME = "ConfigurationDb"; + public const string PRESISTED_GRANTS_CONNECTION_NAME = "PersistedGrantDb"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Constants\IdentityMessagesKeys.cs + +namespace xIds.Constants +{ + public struct IdentityMessagesKeys + { + public const string Terms = "terms"; + public const string InviteMsg = "user_invite_msg"; + public const string RegistrationApproveMsg = "registration_approve_msg"; + public const string RegisteredMsg = "registered_msg"; + public const string VerificationCodeMsg = "verification_code_msg"; + public const string ChangePasswordMsg = "change_password_msg"; + public const string PasswordChangedMsg = "password_changed_msg"; + public const string NewDeviceLoggedInMsg = "new_device_logged_in_msg"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Constants\XDataServiceConstants.cs + +namespace xIds.Constants +{ + /// + /// /// Default Pagination Values ... + /// + public partial struct XDataServiceConstants + { + public static int DEFAULT_PAGE_SIZE = 50; + public static int MAX_AVAILABLE_PAGE_SIZE = 500; + public static int MIN_AVAILABLE_PAGE_SIZE = 20; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Constants\XDbProviderConfigurations.cs + +namespace xIds.Constants +{ + public partial class XDbProviderConfigurations + { + /// + /// Default ConnectionString Name ... + /// + public const string DEFAULT_CONNECTION_NAME = "DataConnection"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Constants\XDbProviders.cs + +namespace xIds.Constants +{ + /// + /// /// Represent Supported DBMS for Managing Data ... + /// + public enum XDbProviders + { + None, + MySQL, + SQLite, + SQLServer, + } + + /// + /// Represent Supported DBMS for Managing Data ... + /// + public partial struct ProviderType + { + public const string MySQL = "MYSQL"; + public const string SQLite = "SQLITE"; + public const string SQLServer = "SQLSERVER"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\Account\AccountController+Admin.cs + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xIdentityHelper; +using xCommons.Attributes; +using xCommons.Extensions; +using xIdentityModels.Dtos; +using static IdentityServer4.IdentityServerConstants; + +namespace xIds.Controllers +{ + public partial class AccountController + { + // + #region Admin Actions ... + /// + /// Ban Specific Users + /// + /// an instance of XUserNameIdRequest which represents user identifier list to Ban + /// a list of banned users identifiers + [RequireXPowered] + [HttpPost("Ban")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledAdmin)] + public async Task>> Ban( + [FromBody] XUserNameIdRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(model) + .AddNotZeroChilds(model.Ids) + .ValidateGroupAsync(); + + // + var result = await IdentityManager + .Ban( + User.Identity.Name, + model + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// UnBann Specific Users + /// + /// an instance of XUserNameIdRequest which represents user identifier list to Ban + /// a list of unbanned users identifiers + [RequireXPowered] + [HttpPost("UnBan")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledAdmin)] + public async Task>> UnBan( + [FromBody] XUserNameIdRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(model) + .AddNotZeroChilds(model.Ids) + .ValidateGroupAsync(); + + // + var result = await IdentityManager + .UnBan( + User.Identity.Name, + model + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Check Specific User is Banned or not + /// + /// specified user's identifier + /// a boolean value which represent user banned or not + [RequireXPowered] + [Authorize(Policy = LocalApi.PolicyName)] + [HttpGet("IsBanned/{userSelectByParam?}")] + [Authorize(Policy = XPolicies.EnabledAdmin)] + [Authorize(Policy = XPolicies.EnabledAgent)] + public async Task> IsBanned( + [FromRoute] string userSelectByParam = null + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (userSelectByParam.IsNullOrEmpty()) + { + userSelectByParam = User.Identity.Name; + } + ValidationProvider.NotEmpty(userSelectByParam); + + // + var result = await IdentityManager + .IsBanned( + User.Identity.Name, + userSelectByParam + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\Account\AccountController+Authentication.cs + +using System; +using System.Threading.Tasks; +using IdentityModel.Client; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xIdentityHelper; +using xCommons.Attributes; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using static IdentityServer4.IdentityServerConstants; +using static xIdentityHelper.XApiScopeHelper; +using Microsoft.Extensions.Logging; + +namespace xIds.Controllers +{ + public partial class AccountController + { + // + #region Authentication Actions ... + /// + /// Retrieve OAuth Discovery Document + /// + /// an instance of DiscoveryDocumentResponse + [AllowAnonymous] + [RequireXPowered] + [HttpGet("DiscoveryDocument")] + public async Task> GetDiscoveryDocument() + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .RequestDiscoveryDocument(); + + // + // Ceck Response Result ... + if (result.IsError) + { + XException.InvalidConfiguration.Throw(); + } + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Request AccessToken for Specific XApiScope + /// + /// a member of XApiScope + /// an instance of XTokenResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("RequestScopeAccessToken")] + public async Task> RequestScopeAccessToken( + [FromHeader] XApiScope scope + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + var scopeName = string.Empty; + try + { + scopeName = scope.GetStringValue(); + } + catch { } + ValidationProvider.NotEmpty(scopeName); + + // + var result = await IdentityManager + .RequestScopeAccessToken(scopeName); + + // + // Ceck Response Result ... + if (result.IsError) + { + XException.InvalidConfiguration.Throw(); + } + + // + return Ok(result + .CreateXTokenResponse() + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Authenticate User + /// + /// + /// Sample request: + /// + /// { + /// "password": "", + /// "userSelectBy": "", + /// } + /// + /// + /// an instance of XLoginRequest class which represent Authentication requirements + /// an instance of XTokenResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("Authenticate")] + public async Task> Authenticate( + [FromBody] XLoginRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(model) + .AddNotEmpty( + model.UserSelectBy, + model.Password + ) + .ValidateGroupAsync(); + + // + var result = await IdentityManager + .Authenticate(model); + + // + // Check Result ... + if (result.IsError) + { + throw result.GetException(); + } + + // + // Return Result ... + return Ok(result + .CreateXTokenResponse() + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Authenticate User + /// + /// + /// Sample request: + /// + /// { + /// "language": "fa-IR" + /// "password": "", + /// "userSelectBy": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XLoginRequest class which represent Authentication requirements + /// an instance of XLoginResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("Login")] + public async Task> Login( + [FromBody] XLoginRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull( + model, + model.Device) + .AddNotEmpty( + model.Language, + model.UserSelectBy, + model.Password + ) + .ValidateGroupAsync(); + + // + var result = await IdentityManager + .Login(model); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Refresh Expired Tokens + /// + /// + /// in addition to AccessToken, you had to pass RefreshToken due to Headers + /// + /// an instance of XTokenResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("RefreshTokens")] + public async Task> RefreshTokens() + { + // + // Do Action ... + try + { + // + // Retrieve Toke Response ... + var model = await RetrieveTokensAsXTokenResponse(); + + // + // Validate Args ... + ValidationProvider + .GroupValidationBuilder() + .AddNotNull(model) + .AddNotEmpty( + model.AccessToken, + model.RefreshToken) + .ValidateGroup(); + + // + var result = await IdentityManager + .RefreshTokens(model); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Password Actions ... + /// + /// Change a User's Password ... + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - Password: user's current active password. + /// - NewPassword: user's new password to change. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Login URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "password": "" + /// "newPassword": "", + /// "returnUrl": "http://localhost/login", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// + [RequireXPowered] + [HttpPost("ChangePassword")] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = XPolicies.EnabledUser)] + [Authorize(Policy = LocalApi.PolicyName)] + public async Task ChangePassword( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull( + model, + model.Device + ) + .AddNotEmpty( + model.Lang, + model.Password, + model.NewPassword, + model.ReturnUrl + ) + .ValidateGroupAsync(); + + // + // Get User Select By ... + var userSelectByParam = GetUserSelectByParam( + model, + forceNotNull: false + ); + if (userSelectByParam.IsNullOrEmpty()) + { + userSelectByParam = User.Identity.Name; + } + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Get Request ... + await IdentityManager + .ChangePassword( + model.Lang, + model.Device, + userSelectByParam, + model.Password, + model.NewPassword, + model.ReturnUrl + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\Account\AccountController+Friendship.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xCommons.Attributes; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityHelper; +using xIdentityModels.Constants; +using xIdentityModels.Dtos; +using xIdentityModels.Navigations; +using xModels.Dtos; +using static IdentityServer4.IdentityServerConstants; + +namespace xIds.Controllers +{ + public partial class AccountController + { + // + #region Friendship Actions ... + // + #region Actions ... + /// + /// Follow a User + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + [RequireXPowered] + [HttpPost("Friendship/{destUser}/Follow")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> Follow( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .Follow( + User.Identity.Name, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Cancel Following + /// + /// a user identifier which represent destination user + /// a boolean value + [HttpPost("Friendship/{destUser}/Cancel")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> Cancel( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .Cancel( + User.Identity.Name, + destUser + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Unfollow a Follower + /// + /// a user identifier which represent destination user + /// + [HttpPost("Friendship/{destUser}/UnFollowFollower")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task UnFollowFollower( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + await IdentityManager + .UnFollowFollower( + User.Identity.Name, + destUser + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Unfollow Following + /// + /// a user identifier which represent destination user + /// + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpPost("Friendship/{destUser}/UnFollowFollowing")] + public async Task UnFollowFollowing( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + await IdentityManager + .UnFollowFollowing( + User.Identity.Name, + destUser + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Block a Follower + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + [HttpPost("Friendship/{destUser}/Block")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> Block( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .Block( + User.Identity.Name, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + + // + return exResult; + } + } + + /// + /// Unblock a Blocked User + /// + /// an instance of XFriendshipFollowing + /// an instance of XFriendshipFollowing + [HttpPost("Friendship/{destUser}/UnBlock")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> UnBlock( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .UnBlock( + User.Identity.Name, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + #endregion + + // + #region Request Handlers ... + /// + /// Accept a Following Request + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollower + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpPost("Friendship/{destUser}/AcceptRequest")] + public async Task> AcceptRequest( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .AcceptRequest( + User.Identity.Name, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Reject a Following Request + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollower + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpPost("Friendship/{destUser}/RejectRequest")] + public async Task> RejectRequest( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .RejectRequest( + User.Identity.Name, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + #endregion + + // + #region Getters ... + /// + /// Check a User IsFollower of Requested User + /// + /// a user identifier which represent destination user + /// a boolean value + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/IsFollower")] + public async Task> IsFollower( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .IsFollower( + User.Identity.Name, + destUser + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Follower State of a User + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipState + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/FollowerState")] + public async Task> FollowerState( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .GetFollowerState( + User.Identity.Name, + destUser + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Specific Follower + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollower + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/Follower")] + public async Task> Follower( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .GetFollower( + User.Identity.Name, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Followers List Of Current User + /// + /// a collection of XFriendshipFollower + [HttpGet("Friendship/Followers")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> Followers() + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .GetFollowers(User.Identity.Name); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + + // + return exResult; + } + } + + /// + /// Get All Followers List Includes Blocked, Requested and etc + /// of Current User + /// + /// a collection of XFriendshipFollower + [HttpGet("Friendship/AllFollowers")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> AllFollowers() + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .GetAllFollowers(User.Identity.Name); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get All Followers List Includes Blocked, Requested and etc + /// of Current User based On Query Model ... + /// + /// a Query Result of XFriendDto + [HttpGet("Friendship/QueryFollowers")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> QueryFollowers( + [FromQuery] XQuery query, + [FromQuery] string destUser + ) + { + // + // Do Action ... + try + { + // + // Normalizing Dest User ... + var userInfo = await GetUserInfo(); + bool isAdmin = userInfo.Roles.Contains(XUserRoles.ADMIN); + if (destUser.IsNullOrEmpty()) + { + destUser = userInfo.UserName; + } + bool isDestUserValid = isAdmin || destUser == userInfo.UserId || destUser == userInfo.Email || destUser == userInfo.PhoneNumber || destUser == userInfo.UserName; + if (!isDestUserValid) + { + XException.NotAllowed.Throw(); + } + + // + var result = await IdentityManager + .QueryFollowers( + query: query, + userSelectByParam: destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Check a User is in Followings of Current User + /// + /// a user identifier which represent destination user + /// a boolean value + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/IsFollowing")] + public async Task> IsFollowing( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .IsFollowing( + User.Identity.Name, + destUser + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Following State Relation between Specific User + /// and Current User + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipState + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/FollowingState")] + public async Task> FollowingState( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .GetFollowingState( + User.Identity.Name, + destUser + ); + + // + return Ok(result); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Specific Following Model + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipFollowing + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpGet("Friendship/{destUser}/Following")] + public async Task> Following( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(destUser); + + // + var result = await IdentityManager + .GetFollowing( + User.Identity.Name, + destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Followings of Current User + /// + /// a collection of XFriendshipFollowing + [HttpGet("Friendship/Followings")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> Followings() + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .GetFollowings(User.Identity.Name); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get All Following List Includes Blocked, Requested and etc + /// of Current User + /// + /// a collection of XFriendshipFollowing + [HttpGet("Friendship/AllFollowings")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> AllFollowings() + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .GetAllFollowings(User.Identity.Name); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get All Following List Includes Blocked, Requested and etc + /// of Current User based On Query Model ... + /// + /// a Query Result of XFriendDto + [HttpGet("Friendship/QueryFollowings")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> QueryFollowings( + [FromQuery] XQuery query, + [FromQuery] string destUser + ) + { + // + // Do Action ... + try + { + // + // Normalizing Dest User ... + var userInfo = await GetUserInfo(); + bool isAdmin = userInfo.Roles.Contains(XUserRoles.ADMIN); + if (destUser.IsNullOrEmpty()) + { + destUser = userInfo.UserName; + } + bool isDestUserValid = isAdmin || destUser == userInfo.UserId || destUser == userInfo.Email || destUser == userInfo.PhoneNumber || destUser == userInfo.UserName; + if (!isDestUserValid) + { + XException.NotAllowed.Throw(); + } + + // + var result = await IdentityManager + .QueryFollowings( + query: query, + userSelectByParam: destUser + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + #endregion + + // + #region Others ... + /// + /// Return Following UserName's List of Current User + /// + /// a collection of UserNames + [HttpGet("Friendship/FollowingsList")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> FollowingsList() + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .GetFollowingList(User.Identity.Name); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Return Followers UserName's List of Current User + /// + /// a collection of UserNames + [HttpGet("Friendship/FollowersList")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> FollowersList() + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .GetFollowersList(User.Identity.Name); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Get Friendship Info Model between Specific User and Current User + /// + /// a user identifier which represent destination user + /// an instance of XFriendshipInfoDto + [HttpGet("Friendship/{destUser}/FriendshipInfo")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> FriendshipInfo( + [FromRoute] string destUser + ) + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .GetFriendshipInfo(User.Identity.Name, destUser); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + #endregion + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\Account\AccountController+Open.cs + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xCommons.Attributes; +using xCommons.Constants; +using xIdentityModels.Dtos; + +namespace xIds.Controllers +{ + public partial class AccountController + { + // + // Implememnt all Open Actions here ... + + // + #region Open Actions ... + [AllowAnonymous] + [RequireXPowered] + [HttpGet("Open")] + public async Task> Get( + [FromHeader] string token, + [FromQuery] string request + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(token) + .AddNotEmpty(request) + .ValidateGroupAsync(); + + // + // Prepare Inner Dto Model ... + var dto = new XOpenActionInnerDto + { + Token = token, + Payload = request + }; + + // + // Do Action ... + var resultDto = await IdentityManager.OpenGet(dto); + Response.Headers.Add(XAuthentication.TOKEN, resultDto.Token); + + // + // Return Result + return Ok(resultDto.Payload); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Private ... + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\Account\AccountController+Profile.cs + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using xIdentityHelper; +using xCommons.Attributes; +using xCommons.Extensions; +using xIdentityModels.Dtos; +using xIdentityModels.Models; +using xModels.Dtos; +using static IdentityServer4.IdentityServerConstants; + +namespace xIds.Controllers +{ + public partial class AccountController + { + // + #region Retrieve Actions ... + /// + /// Retrieve User Names based on UserIds + /// + /// a comma seperated list of UserIds + /// a collection of UserNames + [HttpGet("Profile/{ids}/GetNames")] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> GetNames( + [FromRoute] string ids + ) + { + // + // Do Action ... + try + { + // + ValidationProvider.NotEmpty(ids); + + // + var xIdList = ids.ParseListString(); + var result = await IdentityManager.GetUserNamesAsync( + xIdList + ); + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// get user ids and retrieve corresponding user names + /// + /// an instance of XUserNameIdRequest which represent required UserIds collection + /// a collection of XUserNameIdResponse instance + [HttpPost("Profile/GetNameIds")] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> GetNameIds( + [FromBody] XUserNameIdRequest model + ) + { + // + // Do Action ... + try + { + // + ValidationProvider.NotNull(model); + + // + var result = await IdentityManager.GetUserNameIdsAsync( + model + ); + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get Profile Object of Specific User + /// + /// specifies which user profile must be retrieved + /// an instance of XUserProfileDto + [RequireXPowered] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = LocalApi.PolicyName)] + [HttpGet("Profile/{userSelectByParam?}")] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> GetProfile( + [FromRoute] string userSelectByParam = "" + ) + { + // + // Do Action ... + try + { + // + if (userSelectByParam.IsNullOrEmpty()) + { + userSelectByParam = User.Identity.Name; + } + + // + var result = await IdentityManager + .GetUserProfileAsync( + userSelectByParam, + User.Identity.Name + ); + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Query Users + /// for specifiy users role for query user 'role: string' and 'forceRole: boolean' in header + /// + /// how to filter results based on XQuery structure + /// an string which represent user role + /// if it's true the user must has exact role, otherwise top level users also listed + /// an instance of XQueryResult of XUserProfileDto + [RequireXPowered] + [HttpGet("Profile/Query")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledAdmin)] + [Authorize(Policy = XPolicies.EnabledAgent)] + public async Task>> QueryProfiles( + [FromQuery] XQuery query, [FromHeader] string role, [FromHeader] bool forceRole = false + ) + { + // + // Do Action ... + try + { + // + var userId = User.Identity.Name; + + // + var result = new XQueryResult(); + if (role.IsNullOrEmpty()) + { + // + result = await IdentityManager.QueryUsers( + userId, + query + ); + } + else + { + // + result = await IdentityManager.QueryInRoleUsers( + userId, + role, + query, + forceRole + ); + } + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exception = GetExceptionActionResult(ex); + return exception; + } + } + + /// + /// Query Specified User's Profile Images + /// + /// determine's which user profile must be retrieved + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XProfileImage + [RequireXPowered] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [Authorize(Policy = XPolicies.EnabledAdmin)] + [HttpGet("Profile/Avatars/Query/{userSelectByParam?}")] + public async Task>> QueryAvatars( + [FromQuery] XQuery query, [FromRoute] string userSelectByParam = null + ) + { + // + // Do Action ... + try + { + // + if (userSelectByParam.IsNullOrEmpty()) + { + userSelectByParam = User.Identity.Name; + } + + // + var result = await IdentityManager.QueryAvatars( + userSelectByParam, + query + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exception = GetExceptionActionResult(ex); + return exception; + } + } + #endregion + + // + #region Update Actions ... + /// + /// Update User Profile based on XProfileUpdateRequest (FirstName/LastName/DateOfBirth) + /// + /// user update info, an instance of XProfileUpdateRequest + /// determine's which user profile must be retrieved + /// an instance of XUserProfileDto + [RequireXPowered] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + [HttpPost("Profile/Update/{userSelectByParam?}")] + public async Task> UpdateProfile( + [FromBody] XProfileUpdateRequest model, [FromRoute] string userSelectByParam = null + ) + { + // + // Do Action ... + try + { + // + if (userSelectByParam.IsNullOrEmpty()) + { + userSelectByParam = User.Identity.Name; + } + + // + var result = await IdentityManager.ProfileUpdateAsync( + userSelectByParam, + User.Identity.Name, + model + ); + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update a User Profile based on XProfileUpdateRequest Full + /// + /// user update info, an instance of XProfileUpdateRequest + /// determine's which user profile must be retrieved + /// an instance of XUserProfileDto + [RequireXPowered] + [Authorize(Policy = LocalApi.PolicyName)] + [HttpPost("Profile/{userSelectByParam?}")] + [Authorize(Policy = XPolicies.EnabledAdminOrAgent)] + public async Task> FullUpdateProfile( + [FromBody] XProfileUpdateRequest model, [FromRoute] string userSelectByParam = null + ) + { + // + // Do Action ... + try + { + // + if (userSelectByParam.IsNullOrEmpty()) + { + userSelectByParam = User.Identity.Name; + } + + // + var result = await IdentityManager.FullProfileUpdateAsync( + userSelectByParam, + User.Identity.Name, + model + ); + + // + // Return Result + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Avatar Actions ... + /// + /// Add a New Profile Image + /// + /// an specific File to upload, IFormFile + /// an instance of XUserProfileDto + [RequireXPowered] + [HttpPost("Profile/Avatar")] + [RequestSizeLimit(966_367_641)] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> AddAvatar( + [FromForm] IFormFile file + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotNull(file); + + // + // Get Request ... + var result = await IdentityManager + .AddAvatar( + User.Identity.Name, + file + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Add a New Collection of Profile Images + /// + /// a collection of Files to upload, IFormFileCollection + /// an instance of XUserProfileDto + [RequireXPowered] + [HttpPost("Profile/Avatars")] + [RequestSizeLimit(966_367_641)] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> AddAvatars( + [FromForm] IFormFileCollection files + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + ValidationProvider.NotZeroChilds(files); + + // + // Get Request ... + var result = await IdentityManager.AddAvatars( + User.Identity.Name, + files + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Remove Profile Images + /// + /// a comma seperated list of avatarIds to remove + /// an instance of XUserProfileDto + [RequireXPowered] + [Authorize(Policy = XPolicies.User)] + [HttpDelete("Profile/Avatars/{ids}")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> RemoveAvatars( + [FromRoute] string ids + ) + { + // + // Do Action ... + try + { + // + ValidationProvider.NotEmpty(ids); + var idList = ids.Split(","); + var idCollection = new Collection(); + + // + foreach (var s in idList) + { + idCollection.Add(int.Parse(s)); + } + + // + // Get Request ... + var result = await IdentityManager + .RemoveAvatar( + User.Identity.Name, + idCollection.ToArray() + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// set Specified Profile Image as Avatar + /// + /// an integer which reperesent AvatarId to set as current Avatar + /// an instance of XUserProfileDto + [RequireXPowered] + [HttpPost("Profile/Avatars/{id:int}/Set")] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task> SetAvatar( + [FromRoute] int id + ) + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .SetAvatar( + User.Identity.Name, + id + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exception = GetExceptionActionResult(ex); + return exception; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\Account\AccountController+Query.cs + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xCommons.Attributes; +using xCommons.Extensions; +using xIdentityHelper; +using xModels.Dtos; +using static IdentityServer4.IdentityServerConstants; + +namespace xIds.Controllers +{ + public partial class AccountController + { + /// + /// Query Users + /// for specifiy users role for query user 'role: string' and 'forceRole: boolean' in header + /// + /// how to filter results based on XQuery structure + /// an string which represent user role + /// if it's true the user must has exact role, otherwise top level users also listed + /// an instance of XQueryResult of XUserProfileDto + [RequireXPowered] + [HttpGet("Users/Query")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> QueryUsers( + [FromQuery] XQuery query, + [FromHeader] string role, + [FromHeader] bool forceRole = false + ) + { + // + // Do Actions ... + try + { + // + // Retrieve User Info ... + var userInfo = await GetUserInfo(); + + // + // Retrieve Result Based on Provided Query ... + var result = await IdentityManager.QueryUsers( + role: role, + query: query, + forceRole: forceRole, + requestedUserSelectByParam: userInfo.UserId + ); + + // + // Return result ... + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exception = GetExceptionActionResult(ex); + return exception; + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\Account\AccountController+Registration.cs + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using xIdentityHelper; +using xCommons.Attributes; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Models; +using static IdentityServer4.IdentityServerConstants; + +namespace xIds.Controllers +{ + public partial class AccountController + { + // + #region CanRegister ... + /// + /// Check a UserName is Available For Registration or not + /// + /// an string value which represent desired username + /// a boolean value + [AllowAnonymous] + [RequireXPowered] + [HttpGet("CanRegisterUserName/{userName?}")] + public async Task> CanRegisterUserName( + [FromRoute] string userName + ) + { + // + // Validate Args ... + if (userName.IsNullOrEmpty()) + { + return BadRequest( + XException.InvalidArgs + .ToXError() + ); + } + + // + // Do Action ... + try + { + // + var result = await IdentityManager + .CanRegister(userName); + + // + // Return Result + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + + // + return result; + } + } + + /// + /// Check a Mobile Number is Available For Registration or not + /// + /// an string value which represent desired mobile number + /// a boolean value + [AllowAnonymous] + [RequireXPowered] + [HttpGet("CanRegisterMobileNumber/{mobileNumber?}")] + public async Task> CanRegisterMobileNumber( + [FromRoute] string mobileNumber + ) + { + // + // Validate Args ... + if (mobileNumber.IsNullOrEmpty()) + { + return BadRequest( + XException.InvalidArgs + .ToXError() + ); + } + if (!mobileNumber.IsValidMobileNumber()) + { + return BadRequest( + XException.InvalidMobileNumber + .ToXError() + ); + } + + // + // Do Action ... + try + { + // + var result = await IdentityManager + .CanRegister(mobileNumber); + + // + // Return Result + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + + // + return result; + } + } + + /// + /// Check a Email Address is Available For Registration or not + /// + /// an string value which represent desired email address + /// a boolean value + [AllowAnonymous] + [RequireXPowered] + [HttpGet("CanRegisterEmail/{email?}")] + public async Task> CanRegisterEmail( + [FromRoute] string email + ) + { + // + // Validate Args ... + if (email.IsNullOrEmpty()) + { + return BadRequest( + XException.InvalidArgs + .ToXError() + ); + } + if (!email.IsValidEmail()) + { + return BadRequest( + XException.InvalidEmailAddress + .ToXError() + ); + } + + // + // Do Action ... + try + { + // + var result = await IdentityManager + .CanRegister(email); + + // + // Return Result + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + + // + return result; + } + } + #endregion + + // + #region Confirmations ... + /// + /// Check User is Confirmed Email or not + /// + /// a boolean value + [RequireXPowered] + [HttpGet("IsConfirmedEmail")] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = LocalApi.PolicyName)] + public async Task> IsConfirmedEmail() + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .IsConfirmedEmail(User.Identity.Name); + + // + // Return Result + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + + // + return result; + } + } + + /// + /// Check User is Confirmed Mobile or not + /// + /// a boolean value + [RequireXPowered] + [HttpGet("IsConfirmedMobile")] + [Authorize(Policy = XPolicies.User)] + [Authorize(Policy = LocalApi.PolicyName)] + public async Task> IsConfirmedMobile() + { + // + // Do Action ... + try + { + // + var result = await IdentityManager + .IsConfirmedMobile(User.Identity.Name); + + // + // Return Result + return Ok(result); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + + // + return result; + } + } + + /// + /// Confirm Registration + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Login URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "actionToken": "" + /// "returnUrl": "http://localhost/login", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// + [AllowAnonymous] + [RequireXPowered] + [HttpPost("ConfirmRegistration")] + public async Task ConfirmRegistration( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.ActionToken, + model.ReturnUrl + ) + .AddNotNull(model.Device) + .ValidateGroupAsync(); + + // + // Get Request ... + await IdentityManager + .ConfirmRegistration( + model.Lang, + model.Device, + model.ActionToken, + model.ReturnUrl + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Confirm Requested Mobile Number + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - Device: user's client device which is an instance of XDevice. + /// - MobileVerificationCode: a verification code which produced due Request Action. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "actionToken": "" + /// "mobileVerificationCode": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("ConfirmMobileNumber")] + public async Task> ConfirmMobileNumber( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.ActionToken, + model.MobileVerificationCode + ) + .AddNotNull(model.Device) + .ValidateGroupAsync(); + + // + // Get Request ... + var result = await IdentityManager + .ConfirmMobileNumber( + model.Lang, + model.Device, + model.ActionToken, + model.MobileVerificationCode + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Confirm Requested Email Address + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - Device: user's client device which is an instance of XDevice. + /// - EmailVerificationCode: a verification code which produced due Request Action. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "actionToken": "" + /// "emailVerificationCode": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("ConfirmEmailAddress")] + public async Task> ConfirmEmailAddress( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.ActionToken, + model.EmailVerificationCode + ) + .AddNotNull(model.Device) + .ValidateGroupAsync(); + + // + // Get Request ... + var result = await IdentityManager + .ConfirmEmailAddress( + model.Lang, + model.Device, + model.ActionToken, + model.EmailVerificationCode + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + #endregion + + // + #region Request for Actions ... + /// + /// Request For Mobile Number Change/Confirm + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - Password: user's current active password. + /// - MobileNumber: which phone number is going to confirm. + /// - Device: user's client device which is an instance of XDevice. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "password": "", + /// "mobileNumber": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("RequestConfirmMobile")] + public async Task> RequestConfirmMobile( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.Password, + model.MobileNumber + ) + .AddNotNull(model.Device) + .AddMobileNumber(model.MobileNumber) + .ValidateGroupAsync(); + + // + // Get UserSelect By Param ... + var userSelectByParam = GetUserSelectByParam( + model, + excludes: new List { + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + + // + // Get Request ... + var result = await IdentityManager + .RequestConfirmMobile( + model.Lang, + model.Device, + userSelectByParam, + model.Password, + model.MobileNumber + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Request For Email Address Change/Confirm + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - Password: user's current active password. + /// - Email: which email address is going to confirm. + /// - Device: user's client device which is an instance of XDevice. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "password": "", + /// "email": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("RequestConfirmEmail")] + public async Task> RequestConfirmEmail( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.Email, + model.Password) + .AddNotNull(model.Device) + .AddEmailAddress(model.Email) + .ValidateGroupAsync(); + + // + // Get UserSelect By Param ... + var userSelectByParam = GetUserSelectByParam( + model, + excludes: new List { + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + + // + // Get Request ... + var result = await IdentityManager + .RequestConfirmEmail( + model.Lang, + model.Device, + userSelectByParam, + model.Password, + model.Email + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Request Reset Password Action + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application ResetPassword URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "returnUrl": "http://localhost/reset-password", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("RequestResetPassword")] + public async Task> RequestResetPassword( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.ReturnUrl) + .AddNotNull(model.Device) + .AddUrl(model.ReturnUrl) + .ValidateGroupAsync(); + + // + // Get UserSelect By Param ... + var userSelectByParam = GetUserSelectByParam(model); + + // + // Get Request ... + var result = await IdentityManager + .RequestResetPassword( + model.Lang, + model.Device, + userSelectByParam, + model.ReturnUrl + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Reset a User's Password + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - NewPassword: user's new password to change. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Login URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "actionToken": "", + /// "newPassword": "", + /// "returnUrl": "http://localhost/login", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// + [AllowAnonymous] + [RequireXPowered] + [HttpPost("ResetPassword")] + public async Task ResetPassword( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.ActionToken, + model.NewPassword, + model.ReturnUrl) + .AddNotNull(model.Device) + .ValidateGroupAsync(); + + // + // Get Request ... + await IdentityManager + .ResetPassword( + model.Lang, + model.Device, + model.ActionToken, + model.NewPassword, + model.ReturnUrl + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Request Registration Confirm + /// + /// + /// for this action you have to provide: + /// - a UserSelectedBy identifier which represet User. + /// - Lang: client device currently used locale, such as: en-US. + /// - Password: user's current active password. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Confirm Registration URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "userId": "" + /// "password": "", + /// "returnUrl": "http://localhost/confirm", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// + [AllowAnonymous] + [RequireXPowered] + [HttpPost("RequestConfirmRegistration")] + public async Task RequestConfirmRegistration( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.Password, + model.ReturnUrl) + .AddNotNull(model.Device) + .AddUrl(model.ReturnUrl) + .ValidateGroupAsync(); + + // + // Get UserSelect By Param ... + var userSelectByParam = GetUserSelectByParam(model); + + // + // Get Request ... + await IdentityManager + .RequestConfirmRegistration( + model.Lang, + model.Device, + userSelectByParam, + model.Password, + model.ReturnUrl + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + #endregion + + // + #region Registration ... + /// + /// Invite a User to Register on Dashboard + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - Email: which email address is going to invite. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Registration URL. + /// notice in this step you had to provide all this informations as FormData + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "email": "", + /// "returnUrl": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [RequireXPowered] + [HttpPost("InviteUser")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledAdmin)] + [Authorize(Policy = XPolicies.EnabledAgent)] + public async Task> InviteUser( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + + // + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.Email, + model.ReturnUrl) + .AddEmailAddress(model.Email) + .AddNotNull(model.Device) + .ValidateGroupAsync(); + + // + var result = await IdentityManager + .InviteUser( + model.Lang, + model.Device, + model.Email, + model.ReturnUrl); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Recieve some Basic Informations and Start Registration Proccess + /// if they Valid + /// + /// Registration Proccess Starts with Invoking this Action + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - FirstName: user's FirstName. + /// - LastName: user's LastName. + /// - DateOfBirth: user's dob date. + /// - MobileNumber: user's MobileNumber optional. + /// - Email: user's Email optional. + /// - ActionToken: user's requested action token for this action (if user invited). + /// - Device: user's client device which is an instance of XDevice. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "firstName": "", + /// "lastName": "", + /// "dateOfBirth": "", + /// "email": "", + /// "mobileNumber": "", + /// "actionToken": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("RequestRegistration")] + public async Task> RequestRegistration( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.FirstName, + model.LastName) + .AddNotNull( + model.Device, + model.DateOfBirth) + .ValidateGroupAsync(); + + // + // Check Date Of Birth ... + if (!model.DateOfBirth.HasValue) + { + XException.InvalidArgs.Throw(); ; + } + + // + // Validate Mobile Number if Exists ... + if (!model.MobileNumber.IsNullOrEmpty()) + { + ValidationProvider.MobileNumber(model.MobileNumber); + } + + // + // Validate Email Address if Exists ... + if (!model.Email.IsNullOrEmpty()) + { + ValidationProvider.EmailAddress(model.Email); + } + + // + // Get Request ... + var result = await IdentityManager + .RequestRegistration( + model.Lang, + model.Device, + model.ActionToken, + model.FirstName, + model.LastName, + model.DateOfBirth.GetValueOrDefault(), + model.MobileNumber, + model.Email + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + + // + return exResult; + } + } + + /// + /// Add User Account Informations + /// + /// + /// for this action you have to provide: + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - UserName: user name. + /// - Password: assigne password. + /// - Device: user's client device which is an instance of XDevice. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "userName": "", + /// "password": "", + /// "actionToken": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("AddAccountInfo")] + public async Task> AddAccountInfo( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.ActionToken, + model.UserName, + model.Password) + .AddNotNull(model.Device) + .ValidateGroupAsync(); + + // + // Get Request ... + var result = await IdentityManager + .AddAcountInfo( + model.Lang, + model.Device, + model.ActionToken, + model.UserName, + model.Password); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Upload and Attach a Profile Image for User. this is an Optional Step + /// + /// user's requested action token + /// an instance of IFormFile for user's Avatar + /// an instance of XActionResponse + [AllowAnonymous] + [RequireXPowered] + [HttpPost("AttachProfileImage")] + public async Task> AttachProfileImage( + [FromForm] string actionToken, [FromForm] IFormFile file + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(actionToken) + .AddNotNull(file) + .ValidateGroupAsync(); + + // + var result = await IdentityManager + .AttachProfileImage(actionToken, file); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + return exResult; + } + } + + /// + /// Finish Registrationn Proccess + /// + /// + /// for this action you have to provide: + /// - a proper User Identifier such as: (UserId, UserName, Email or MobileNumber). + /// - Lang: client device currently used locale, such as: en-US. + /// - ActionToken: user's requested action token for this action. + /// - Password: assigne password. + /// - Device: user's client device which is an instance of XDevice. + /// - ReturnUrl: client application Login URL. + /// + /// Sample request: + /// + /// { + /// "lang": "fa-IR", + /// "userName": "", + /// "password": "", + /// "returnUrl": "", + /// "device": { + /// "os":"Mac", + /// "browser":"Chrome", + /// "osVersion":"mac-os-x-15", + /// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36", + /// "deviceType":3, + /// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]", + /// "token":"96f400dd9aa5c57a8cec9d6f4775bad3" + /// } + /// } + /// + /// + /// an instance of XActionRequest class which provider requirement for action + /// + [AllowAnonymous] + [RequireXPowered] + [HttpPost("FinishRegistration")] + public async Task FinishRegistration( + [FromBody] XActionRequest model + ) + { + // + // Do Action ... + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + model.Lang, + model.Password, + model.ReturnUrl) + .AddNotNull(model.Device) + .AddUrl(model.ReturnUrl) + .ValidateGroupAsync(); + + // + var userSelectByParam = IdentityManager + .GetUserSelectByParam(model); + ValidationProvider + .NotEmpty(userSelectByParam); + + // + // Get Request ... + await IdentityManager + .FinishRegistration( + model.Lang, + model.Device, + userSelectByParam, + model.Password, + model.ReturnUrl + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var exResult = GetExceptionActionResult(ex); + + // + return exResult; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\Account\AccountController+Search.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xCommons.Attributes; +using xCommons.Extensions; +using xIdentityHelper; +using xIdentityModels.Dtos; +using xModels.Dtos; +using static IdentityServer4.IdentityServerConstants; + +namespace xIds.Controllers +{ + /// + /// Actions which related to Search and Suggest Users for + /// Friendship or Detecting ... + /// + public partial class AccountController + { + // + #region Actions of Search ... + /// + /// Query Open To Search Users Profiles ... + /// Query all Users Which their Profiles is Open To Search ... + /// + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XUserProfileDto + [RequireXPowered] + [HttpGet("OpenToSearch/Query")] + [Authorize(Policy = LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public async Task>> QueryOpenToSearchProfiles( + [FromQuery] XQuery query) + { + // + // Do Action ... + try + { + // + var userId = User.Identity.Name; + + // + var result = await IdentityManager.QueryOpenToSearchUsers( + userId, + query + ); + + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var exception = GetExceptionActionResult(ex); + return exception; + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\Account\AccountController+Test.cs + +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using xIdentityHelper; +using xCommons.Attributes; +using static IdentityServer4.IdentityServerConstants; + +namespace xIds.Controllers +{ + public partial class AccountController + { + + // + #region Test Actions ... + /// + /// API Read Scope + /// + /// string message + [RequireXPowered] + [Authorize(LocalApi.PolicyName)] + [HttpGet("Test/PassReadAccess")] + [Authorize(Policy = XPolicies.ReadAccess)] + public ActionResult PassReadAccess() + { + return Ok("Read Access Passed ..."); + } + + /// + /// API Write Scope + /// + /// string message + [RequireXPowered] + [Authorize(LocalApi.PolicyName)] + [HttpGet("Test/PassWriteAccess")] + [Authorize(Policy = XPolicies.WriteAccess)] + public ActionResult PassWriteAccess() + { + return Ok("Write Access Passed ..."); + } + + /// + /// API Admin Scope + /// + /// string message + [RequireXPowered] + [Authorize(LocalApi.PolicyName)] + [HttpGet("Test/PassAdminAccess")] + [Authorize(Policy = XPolicies.AdminAccess)] + public ActionResult PassAdminAccess() + { + return Ok("Admin Access Passed ..."); + } + + /// + /// API Manage Scop + /// + /// string message + [RequireXPowered] + [Authorize(LocalApi.PolicyName)] + [HttpGet("Test/PassManageAccess")] + [Authorize(Policy = XPolicies.ManageAccess)] + public ActionResult PassManageAccess() + { + return Ok("Manage Access Passed ..."); + } + + /// + /// a simple Action which returns a List of Authenticated User Claims + /// + /// string message which represent current user's claims + [RequireXPowered] + [HttpGet("Test/HiClaims")] + [Authorize(LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.User)] + public ActionResult HiClaims() + { + // + var result = new + { + name = User.Identity.Name, + claims = User.Claims.Select(c => new + { + c.Type, + c.Value + }) + }; + + // + return Ok(result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [RequireXPowered] + [HttpGet("Test/HiUser")] + [Authorize(LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.User)] + public ActionResult HiUser() + { + // + var result = $"Hi User: {User.Identity.Name} ..."; + + // + return Ok(result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [RequireXPowered] + [HttpGet("Test/HiEnabledUser")] + [Authorize(LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledUser)] + public ActionResult HiEnabledUser() + { + // + var result = $"Hi User: {User.Identity.Name} is Enabled ..."; + + // + return Ok(result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [RequireXPowered] + [HttpGet("Test/HiAdmin")] + [Authorize(LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.Admin)] + public ActionResult HiAdmin() + { + // + var result = $"Hi Admin: {User.Identity.Name} ..."; + + // + return Ok(result); + } + + /// + /// a simple Hello User for Checking Authentication and Policy + /// + /// string message which contains authenticated user name + [RequireXPowered] + [HttpGet("Test/HiEnabledAdmin")] + [Authorize(LocalApi.PolicyName)] + [Authorize(Policy = XPolicies.EnabledAdmin)] + public ActionResult HiEnabledAdmin() + { + // + var result = $"Hi Admin: {User.Identity.Name} is Enabled ..."; + + // + return Ok(result); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\Base\XIBaseController.cs + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Constants; +using xCommons.Controllers; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Models; +using xIds.Interfaces; + +namespace xIds.Controllers.Base +{ + public abstract class XIBaseController : XBaseController + { + public IXIdentityManager IdentityManager { get; } + + protected XIBaseController( + ILogger logger, + XAppConfiguration appConfiguration, + IXIdentityManager identityManager, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { + IdentityManager = identityManager; + } + + // + #region User Handlers NonActions ... + /// + /// Retrieve User Identifier Base on XActionRequest + /// + /// + /// + /// + /// + [NonAction] + public string GetUserSelectByParam( + XActionRequest model, + bool forceNotNull = true, + ICollection excludes = null) + { + // + // Validate Args ... + ValidationProvider.NotNull(model); + + // + var result = IdentityManager.GetUserSelectByParam( + model, + forceNotNull, + excludes); + + // + return result; + } + + /// + /// Retrieve Access Token + /// + /// + [NonAction] + public async Task GetAccessToken() + { + // + var accessToken = Request.Headers[XAuthorization.Header].ToString(); + if (accessToken.IsNullOrEmpty()) + { + accessToken = await HttpContext.GetTokenAsync(XAuthorization.AccessToken); + } + + // + if (accessToken + .ToNormalString() + .Contains(XAuthorization.TokenIdentifier.ToNormalString())) + { + accessToken = accessToken.Remove(0, XAuthorization.TokenIdentifier.Length); + } + + // + Logger.LogInformation($"Token: {accessToken}"); + return accessToken; + } + + /// + /// Retrieve Refresh Token + /// + /// + [NonAction] + public async Task GetRefreshToken() + { + // + var refreshToken = Request.Headers[XAuthorization.RefreshToken].ToString(); + if (refreshToken.IsNullOrEmpty()) + { + refreshToken = await HttpContext.GetTokenAsync(XAuthorization.RefreshToken); + } + + // + Logger.LogInformation($"Refresh Token: {refreshToken}"); + return refreshToken; + } + + /// + /// Retrieve Access Token Expiration Date + /// + /// + [NonAction] + public async Task GetTokenExpiresAt() + { + // + var expiresAtStr = Request.Headers[XAuthorization.ExpiresAt].ToString(); + if (expiresAtStr.IsNullOrEmpty()) + { + expiresAtStr = await HttpContext.GetTokenAsync(XAuthorization.ExpiresAt); + } + + // + var expiresAt = expiresAtStr.ConvertTo(); + + // + Logger.LogInformation($"Token ExpiresAt: {expiresAtStr}"); + return expiresAt; + } + + /// + /// Retrieve All Required Tokens + /// + /// + [NonAction] + public async Task RetrieveTokensAsXLoginResponse() + { + // + var accessToken = await GetAccessToken(); + var refreshToken = await GetRefreshToken(); + var expiresAt = await GetTokenExpiresAt(); + + // + return new XLoginResponse + { + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresAt = expiresAt + }; + } + + /// + /// Retrieve All Required Tokens + /// + /// + [NonAction] + public async Task RetrieveTokensAsXTokenResponse() + { + // + var accessToken = await GetAccessToken(); + var refreshToken = await GetRefreshToken(); + var expiresAt = await GetTokenExpiresAt(); + + // + return new XTokenResponse + { + AccessToken = accessToken, + RefreshToken = refreshToken, + ExpiresAt = expiresAt + }; + } + + /// + /// Retrive UserInfo + /// + /// + [NonAction] + public async Task GetUserInfo() + { + // + var claims = User.Claims ?? null; + if (!claims.HasChild()) + { + return null; + } + + var xTokens = await RetrieveTokensAsXTokenResponse(); + var result = new XUserClaimsInfoDto( + xTokens.AccessToken, + xTokens.RefreshToken, + xTokens.ExpiresAt, + User.Claims + ); + + // + return result; + } + + /// + /// Validate User Authenticated and Retrieve User Info + /// + /// + [NonAction] + public async Task ValidateAndGetUserInfo() + { + // + if (!User.Identity.IsAuthenticated) + { + XException.NotAuthorized.Throw(); + } + + // + var result = await GetUserInfo(); + if (result.IsNull()) + { + XException.NotAuthorized.Throw(); + } + + // + return result; + } + #endregion + + // + #region NonActions ... + /// + /// Convert an Exception to Propper Error Result + /// + /// + /// + [NonAction] + public new ActionResult GetExceptionActionResult(Exception ex) + { + // + var exception = GetExceptionResult(ex); + var error = ex.Message.ToXError(); + + // + Logger.LogError($"exception: {exception}, error: {error}"); + + // + try + { + var xError = exception.Message.ToXError(); + var xException = (XException)xError.Id; + + // + switch (xException) + { + // + case XException.NotFound: + return NotFound(error); + + // + case XException.NotAuthorized: + return Unauthorized(error); + } + } + catch { } + + // + return BadRequest(error); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\AccountController.cs + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Providers; +using xIds.Controllers.Base; +using xIds.Interfaces; + +namespace xIds.Controllers +{ + /// + /// Provide all available tools for manipulating users and accounts + /// + [ApiController] + public partial class AccountController : XIBaseController + { + + public AccountController( + IXIdentityManager identityManager, + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + identityManager, + validationProvider + ) + { } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Controllers\StartupController.cs + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Controllers; +using xCommons.Providers; + +namespace xIds.Controllers +{ + /// + /// Startup and Running Controller + /// + [Route("")] + [AllowAnonymous] + public class StartupController : XBaseController + { + public StartupController( + ILogger logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider + ) : base( + logger, + appConfiguration, + validationProvider + ) + { } + + /// + /// Show Configured Welcome Message + /// + /// an string message + [HttpGet("")] + [AllowAnonymous] + public virtual ActionResult Index() + { + // + var controllerName = GetControllerName(); + var message = $"{AppConfiguration.WelcomeMessage}"; + + // + return Ok(message); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\DbContext\XIdentityDbContext.cs + +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using xIdentityModels; +using xIdentityModels.Models; +using xIdentityModels.Navigations; + +namespace xIds.DbContext +{ + public class XIdentityDbContext : IdentityDbContext + { + // + #region Props/DbSets ... + public DbSet Tokens { get; set; } + public DbSet Devices { get; set; } + public DbSet Avatars { get; set; } + public DbSet BannedDevices { get; set; } + public DbSet Followers { get; set; } + public DbSet Followings { get; set; } + public DbSet VerificationRequests { get; set; } + #endregion + + public XIdentityDbContext(DbContextOptions options) : base(options) + { + // + try + { + Database.EnsureCreated(); + Database.Migrate(); + } + catch { } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // + base.OnModelCreating(modelBuilder); + + // + // Fix XUser GUID Generator on Add ... + modelBuilder + .Entity() + .Property(e => e.Id) + .ValueGeneratedOnAdd(); + + // + // Handle Role List ... + modelBuilder.Entity(u => + { + u.HasMany(x => x.Roles) + .WithOne() + .HasForeignKey(ur => ur.UserId) + .IsRequired(); + }); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\DI\XDIHelperExtension.cs + +using System.Collections.Generic; +using IdentityServer4; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using xCommons.Authorization; +using xCommons.Constants; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityHelper; +using xIdentityModels; +using xIdentityModels.Configurations; +using xIds.Configurations; +using xIds.Constants; +using xIds.DbContext; +using xIds.Extensions; +using xIds.Helpers; +using xIds.Interfaces; +using xIds.Providers; +using xIds.Validators; +using xMessageService.DI; +using xMessageService.Interfaces; +using xStorageService.DI; +using xStorageService.Interfaces; + +namespace xIds.DI +{ + public static class XDIHelperExtension + { + /// + /// Register Application DataProvider + /// + /// + /// + public static void AddXDataProvider( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + // Retrieve DbInfo ... + var dbInfo = configuration.GetXDbInfo(); + var identityDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.IDENTITY_CONNECTION_NAME); + + // + // Register Db Context for EF ... + services.AddDbContext(builder => + { + // + builder.PrepareXDbContextOptionsBuilder( + dbInfo, + identityDbConnectionString + ); + }); + } + + /// + /// Extract Connection String From IConfiguration + /// + /// + /// + /// + public static string GetXConnectionString( + this IConfiguration config, + string connectionName = null + ) + { + // + if (connectionName.IsNullOrEmpty()) + { + connectionName = XDbProviderConfigurations.DEFAULT_CONNECTION_NAME; + } + + // + return config.GetConnectionString(connectionName); + } + + /// + /// Retrive XDataService Configurations + /// + /// + /// + /// + public static XDataServiceConfiguration GetXDataServiceConfiguration( + this IConfiguration source, + string connectionName = null + ) + { + // + var xDataServiceConfigSection = source + .GetSection(Constants.ConfigurationNodeNames.DATA_SERVICE_NODE_NAME); + var result = xDataServiceConfigSection.Get(); + if (result.IsNull()) + { + result = new XDataServiceConfiguration(); + } + + // + result.Provider = source.GetXDbProviderType(); + result.ConnectionString = source.GetXConnectionString(connectionName); + + // + return result; + } + + /// + /// Register XDataService Configuration + /// + /// + /// + /// + public static void AddXDataServiceConfiguration( + this IServiceCollection services, + IConfiguration configuration, + string connectionName = null + ) + { + // + var dataServiceConfiguration = configuration + .GetXDataServiceConfiguration(connectionName); + + // + services.AddSingleton(dataServiceConfiguration); + } + + /// + /// Register All Requirements For XIdentityServer Usage + /// + /// + /// + public static void AddXIdentityServerRequirements( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + services.AddXDataProvider(configuration); + + // + // Register XIdentityMessageProvider ... + services.AddXIdentityMessageProvider(); + + // + // Register IdentityManager Service ... + var xIdentityManager = services.GetRegisteredService(); + if (xIdentityManager.IsNull()) + { + services.AddXIdentityManager(); + } + + // + // Check XMessage Service Registered or not and Register it if not ... + var xMessageProvider = services.GetRegisteredService(); + if (xMessageProvider.IsNull()) + { + services.AddXMessageService(configuration); + } + + // + // Check Storage Service Registered or not and Register it if not ... + var xStorageProvider = services.GetRegisteredService(); + if (xStorageProvider.IsNull()) + { + services.AddXStorageService(configuration); + } + + // + // Register XIdentityConfiguration ... + var xIdentityConfiguration = configuration.GetXIdentityConfiguration(); + services.AddSingleton(xIdentityConfiguration); + + // + // Register XIdentityHelper ... + services.AddSingleton(); + var xIdentityHelper = services.GetRegisteredService(); + + // + // Register Asp.net Identity ... + services.AddXIdentity(); + } + + /// + /// Add IdentityServer with Support of Ef + /// + /// + /// + public static void AddEfSupportXIdentityServer( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + // Register All Requirements ... + services.AddXIdentityServerRequirements(configuration); + + // + // add DbSeeder Config ... + services.AddXDebSeederDescriptor(configuration); + + // + // Retrieve Certificate ... + var identityServerSigningCertificate = configuration.GetCertificate(CertificatesConfigurations.CertificateNames.IdentityServerSigning); + + // + // Retrieve DbInfo ... + var dbInfo = configuration.GetXDbInfo(); + + // + // Retrieve Connection String Names ... + var configDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.CONFIGURATION_CONNECTION_NAME); + var persistedGrantDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.PRESISTED_GRANTS_CONNECTION_NAME); + + // + // Register IdentityServer ... + services.AddIdentityServer(options => + { + // + options.Discovery.CustomEntries.Add("account_api", "~/account"); + + // + // options.Events.RaiseErrorEvents = true; + // options.Events.RaiseFailureEvents = true; + // options.Events.RaiseSuccessEvents = true; + // options.Events.RaiseInformationEvents = true; + }) + .LoadSigningCredentialFrom(identityServerSigningCertificate) + .AddXDbProvider(configuration) + .AddAspNetIdentity() + .AddProfileService() + .AddResourceOwnerValidator(); + } + + /// + /// force app to use IdentityServer + /// + /// + /// + public static void UseXIdentityServer( + this IApplicationBuilder app, + ILogger logger + ) + { + // + // Update XIdentityServer Configurations before Use Identity Server ... + app.SeedData(logger) + .GetAwaiter() + .GetResult(); + + // + // Use Identity Server ... + app.UseIdentityServer(); + } + + /// + /// Register Asp.net Identity as User Store + /// + /// + public static void AddXIdentity(this IServiceCollection services) + { + // + var xIdentityConfiguration = services.GetRegisteredService(); + if (xIdentityConfiguration.IsNull()) + { + XException.InvalidConfiguration.Throw(); + } + + // + var xIdentityHelper = services.GetRegisteredService(); + if (xIdentityHelper.IsNull()) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Add Identity for User Persists ... + services.AddIdentity(options => + { + // + var mLockoutSpan = xIdentityHelper.GetLockoutTimeSpan(); + + // + // Lockout Setting ... + options.Lockout.DefaultLockoutTimeSpan = mLockoutSpan; + options.Lockout.MaxFailedAccessAttempts = xIdentityConfiguration.Policy.Lockout.MaxFailedAccessAttempts; + options.Lockout.AllowedForNewUsers = xIdentityConfiguration.Policy.Lockout.AllowedForNewUsers; + + // + // Password settings ... + options.Password.RequireDigit = xIdentityConfiguration.Policy.Password.RequireDigit; + options.Password.RequireLowercase = xIdentityConfiguration.Policy.Password.RequireLowercase; + options.Password.RequireNonAlphanumeric = xIdentityConfiguration.Policy.Password.RequireNonAlphanumeric; + options.Password.RequireUppercase = xIdentityConfiguration.Policy.Password.RequireUppercase; + options.Password.RequiredLength = xIdentityConfiguration.Policy.Password.RequiredLength; + options.Password.RequiredUniqueChars = xIdentityConfiguration.Policy.Password.RequiredUniqueChars; + + // + // SignIn settings ... + options.SignIn.RequireConfirmedEmail = xIdentityConfiguration.Policy.SignIn.RequireConfirmedEmail; + options.SignIn.RequireConfirmedPhoneNumber = xIdentityConfiguration.Policy.SignIn.RequireConfirmedPhoneNumber; + + // + // User settings ... + options.User.AllowedUserNameCharacters = xIdentityConfiguration.Policy.User.AllowedUserNameCharacters; + options.User.RequireUniqueEmail = xIdentityConfiguration.Policy.User.RequireUniqueEmail; + }) + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + } + + /// + /// Register IdentityServer Based Authentication + /// + /// + /// + public static void AddXIdentityServerAuthentication( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + services.AddXIdentityResourceConfiguration(configuration); + services.AddAuthentication() + .AddJwtBearer(options => + { + // + options.SaveToken = true; + options.RequireHttpsMetadata = false; + + // + options.ForwardDefault = XAuthentication.IDENTITY_SERVER_LOCAL_API; + }) + .AddLocalApi(); + } + + /// + /// Register Authorization Policies + /// + /// + /// + public static void AddXAuthorization( + this IServiceCollection services, + IDictionary policies = null + ) + { + // + services.AddAuthorization(options => + { + // + if (!policies.IsNull()) + { + // + // Fill Policies ... + using (var policiesEnumerator = policies.GetEnumerator()) + { + while (policiesEnumerator.MoveNext()) + { + var policyDescriptor = policiesEnumerator.Current; + options.AddPolicy(policyDescriptor.Key, policyDescriptor.Value); + } + } + } + + // + var xPolicies = XAuthorizationHelper.GetXAuthorizationPolicies(); + if (!xPolicies.IsNull()) + { + // + // Fill Policies ... + using (var policiesEnumerator = xPolicies.GetEnumerator()) + { + while (policiesEnumerator.MoveNext()) + { + var policyDescriptor = policiesEnumerator.Current; + options.AddPolicy(policyDescriptor.Key, policyDescriptor.Value); + } + } + } + + // + // Add Local APi Policy ... + options.AddPolicy(IdentityServerConstants.LocalApi.PolicyName, policy => + { + // + policy.AddAuthenticationSchemes(IdentityServerConstants.LocalApi.AuthenticationScheme); + policy.RequireAuthenticatedUser(); + }); + }); + + // + services.AddSingleton(); + } + + /// + /// Use Forward Headers Options for resolving behind a proxy issues ... + /// + /// + /// + public static void UseXForwardOptions(this IApplicationBuilder source) + { + // + var forwardOptions = new ForwardedHeadersOptions + { + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto, + RequireHeaderSymmetry = false + }; + + // + forwardOptions.KnownNetworks.Clear(); + forwardOptions.KnownProxies.Clear(); + + // + // ref: https://github.com/aspnet/Docs/issues/2384 + source.UseForwardedHeaders(forwardOptions); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Extensions\DbExtensions.cs + +using System; +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using MySql.EntityFrameworkCore.Infrastructure; +using xCommons.Extensions; +using xIdentityModels.Configurations; +using xIds.Configurations; +using xIds.Constants; +using xIds.Models; + +namespace xIds.Extensions +{ + public static class DbExtensions + { + /// + /// Retrieve Data Provider Type from Configurations + /// + /// + /// + public static XDbProviders GetXDbProviderType(this IConfiguration source) + { + // + var dbProvider = (source[$"{ConfigurationNodeNames.PROVIDER_NODE_NAME}"]) + .ToNormalString(); + + // + return dbProvider == ProviderType.SQLServer.ToNormalString() ? + XDbProviders.SQLServer : + dbProvider == ProviderType.SQLite.ToNormalString() ? + XDbProviders.SQLite : + dbProvider == ProviderType.MySQL.ToNormalString() ? + XDbProviders.MySQL : + XDbProviders.None; + } + + /// + /// Retrieve AspNet Identity Configurations + /// + /// + /// + public static XIdentityConfiguration GetXIdentityConfiguration(this IConfiguration source) + { + // + var xIdentityConfigSection = source + .GetSection(ConfigurationNodeNames.IDENTITY_NODE_NAME); + return xIdentityConfigSection.Get(); + } + + /// + /// Retrieve XIdentityResource Configuration from AppSettings + /// + /// + /// + public static XIdentityResourceConfiguration GetXIdentityResourceConfiguration(this IConfiguration configuration) + { + // + var xIdentityResourceSection = configuration.GetSection(ConfigurationNodeNames.IDENTITY_RESOURCE_NODE_NAME); + return xIdentityResourceSection.Get(); + } + + /// + /// Register XIdentityResourceConfiguration + /// + /// + /// + public static void AddXIdentityResourceConfiguration( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + var xIdentityResourceConfiguration = configuration.GetXIdentityResourceConfiguration(); + if (!xIdentityResourceConfiguration.IsNull()) + { + services.AddSingleton(xIdentityResourceConfiguration); + } + } + + /// + /// Converts DbInfo Options Builder for MySql Usage + /// + /// + /// + public static Action GetMySqlOptionsBuilder(this XDbInfo source) + { + return ((Action)source.OptionsBuilder); + } + + /// + /// Converts DbInfo Options Builder for SQLite Usage + /// + /// + /// + public static Action GetSQLiteOptionsBuilder(this XDbInfo source) + { + return ((Action)source.OptionsBuilder); + } + + /// + /// Converts DbInfo Options Builder for SQLServer Usage + /// + /// + /// + public static Action GetSQLServerOptionsBuilder(this XDbInfo source) + { + return ((Action)source.OptionsBuilder); + } + + /// + /// Retrieve Required Informations to Register DbContexts into Di as XDbInfo instance + /// + /// + /// + public static XDbInfo GetXDbInfo(this IConfiguration configuration) + { + return new XDbInfo + { + ProviderType = configuration.GetXDbProviderType(), + MigrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name + }; + } + + /// + /// Prepare DbContextOptionsBuilder with Propper data to Support Configured DbProvider + /// + /// + /// + /// + public static void PrepareXDbContextOptionsBuilder( + this DbContextOptionsBuilder source, + XDbInfo dbInfo, + string connectionString + ) + { + // + if (dbInfo.ProviderType == XDbProviders.MySQL) + { + // + // Add Support for MySql ... + source.UseMySQL( + connectionString, + dbInfo.GetMySqlOptionsBuilder() + ); + } + else if (dbInfo.ProviderType == XDbProviders.SQLite) + { + // + // Add Support for SQLite ... + source.UseSqlite( + connectionString, + dbInfo.GetSQLiteOptionsBuilder() + ); + } + else if (dbInfo.ProviderType == XDbProviders.SQLServer) + { + // + // Add Support for SQLServer ... + source.UseSqlServer( + connectionString, + dbInfo.GetSQLServerOptionsBuilder() + ); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Extensions\IdentityExtensions.cs + +using Microsoft.Extensions.DependencyInjection; +using xIds.Interfaces; +using xIds.Providers; + +namespace xIds.Extensions +{ + public static class IdentityExtensions + { + /// + /// Register IdentityManager + /// + /// + public static void AddXIdentityManager(this IServiceCollection source) + { + source.AddScoped(); + } + + /// + /// Register IdentityMessage Provider + /// + /// + public static void AddXIdentityMessageProvider( + this IServiceCollection source + ) + { + source.AddScoped(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Extensions\IIdentityServerBuilderExtensions.cs + +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xCommons.Models; +using xIds.Constants; + +namespace xIds.Extensions +{ + public static class IIdentityServerBuilderExtensions + { + /// + /// Add Support for Configured XDbProvider to IdentityServer + /// + /// + /// + /// + public static IIdentityServerBuilder AddXDbProvider( + this IIdentityServerBuilder source, + IConfiguration configuration + ) + { + // + // Retrieve DB Info ... + var dbInfo = configuration.GetXDbInfo(); + dbInfo.OptionsBuilder = (optionsBuilder) => + { + optionsBuilder.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); + }; + + // + // Retrieve Connection String Names ... + var identityDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.IDENTITY_CONNECTION_NAME); + var configDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.CONFIGURATION_CONNECTION_NAME); + var persistedGrantDbConnectionString = configuration.GetConnectionString(ConnectionStringNames.PRESISTED_GRANTS_CONNECTION_NAME); + + // + // Add Operational Store ... + source.AddOperationalStore(opt => + { + opt.ConfigureDbContext = + builder => + { + // + builder.PrepareXDbContextOptionsBuilder( + dbInfo, + persistedGrantDbConnectionString + ); + + // + builder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + }; + + // + // this enables automatic token cleanup. this is optional. + opt.EnableTokenCleanup = true; + opt.TokenCleanupInterval = 3600; + }); + + // + // Add Configuration Store ... + source.AddConfigurationStore(opt => + { + opt.ConfigureDbContext = + builder => + { + // + builder.PrepareXDbContextOptionsBuilder( + dbInfo, + configDbConnectionString + ); + }; + }); + + // + return source; + } + + /// + /// Set Configured Certificate File to IdentityServer + /// + /// + /// + /// + public static IIdentityServerBuilder LoadSigningCredentialFrom( + this IIdentityServerBuilder builder, + XCertificate certificate + ) + { + // + if (!certificate.IsNull() && + !certificate.Path.IsNullOrEmpty() && + !certificate.Secret.IsNullOrEmpty() + ) + { + try + { + builder.AddSigningCredential(new X509Certificate2(certificate.Path, certificate.Secret)); + } + catch + { + builder.AddDeveloperSigningCredential(); + } + } + else + { + builder.AddDeveloperSigningCredential(); + } + + // + return builder; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Extensions\IQueryableExtensions.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using xCommons.Extensions; +using xExceptions.Constants; +using xModels.Base; +using xIds.Constants; + +namespace xIds.Extensions +{ + public static class IQueryableExtensions + { + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Extensions\XDbSeederExtensions.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using IdentityServer4.EntityFramework.DbContexts; +using IdentityServer4.EntityFramework.Mappers; +using Microsoft.AspNetCore.Builder; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xIdentityModels.Descriptors; +using xIdentityModels.Extensions; +using xIds.Constants; +using xIds.Interfaces; +using xIds.Models; + +namespace xIds.Extensions +{ + public static class XDbSeeder + { + /// + /// Retrieve DbSeedDescriptor from appSetting Configuration + /// + /// + /// + public static XDbSeedDescriptor GetXDbSeedDescriptor(this IConfiguration configuration) + { + // + var dbSeedSection = configuration.GetSection(ConfigurationNodeNames.DB_SEED_NODE_NAME); + return dbSeedSection.Get(); + } + + /// + /// Register XDbSeedDescriptor as Singleton Service + /// + /// + /// + public static void AddXDebSeederDescriptor( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + var dbSeedDescriptor = configuration.GetXDbSeedDescriptor(); + if (!dbSeedDescriptor.IsNull()) + { + services.AddSingleton(dbSeedDescriptor); + } + else + { + Console.WriteLine("Error: DbSeeder Configuration not found in AppSetting ..."); + } + } + + /// + /// Add Base Required Data to XIdentitySer DbContexts + /// + /// + /// + /// + public static async Task SeedData( + this IApplicationBuilder app, + ILogger logger + ) + { + // + logger.LogInformation($"Start Add/Update XIdentityServer Descriptors ..."); + + // + // Create Scope ... + using (var scope = app.ApplicationServices.CreateScope()) + { + // + // Retrieve Required Configurations ... + var dbSeedDescriptor = scope.ServiceProvider.GetService(); + if (dbSeedDescriptor.IsNull()) + { + // + Console.WriteLine("DbSeeder is Null ..."); + return; + } + logger.LogInformation($"Update Exists Descriptors: {dbSeedDescriptor.UpdateExists.ToString()}"); + + // + // Retrieve Required Services ... + var identityProvider = scope.ServiceProvider.GetService(); + + // + // Retrieve DbContexts ... + var grantDbContext = scope.ServiceProvider.GetService(); + var configDbContext = scope.ServiceProvider.GetService(); + + // + #region Clients ... + // + logger.LogInformation($"Start Seeding Clients ..."); + + // + var dClients = dbSeedDescriptor.Clients; + var hasClient = configDbContext.Clients.Any(); + if ((!hasClient || + (hasClient && dbSeedDescriptor.UpdateExists)) && + dClients.HasChild()) + { + // + var clientEntities = dClients.Select(dc => dc.ToEntity()); + + // + foreach (var client in clientEntities) + { + // + logger.LogInformation($"Add/Update: {client.ClientId}"); + + // + // Retrieve Exists Client ... + var existsClient = configDbContext.Clients + .Include(c => c.AllowedCorsOrigins) + .Include(c => c.AllowedGrantTypes) + .Include(c => c.AllowedScopes) + .Include(c => c.Claims) + .Include(c => c.ClientSecrets) + .FirstOrDefault(cl => cl.ClientId == client.ClientId); + + // + var isExistsClient = !existsClient.IsNull(); + var isSameContent = isExistsClient && + existsClient.IsSameContent(client, propertyBlackList: new[] { nameof(existsClient.Id) }); + + // + if (isExistsClient) + { + // + if (!isSameContent) + { + // + logger.LogInformation($"Update Client: {existsClient.ClientId}"); + + // + existsClient = existsClient.UpdateData(client, propertyBlackList: new[] { nameof(existsClient.Id) }); + configDbContext.Clients.Update(existsClient); + } + } + else + { + // + logger.LogInformation($"Add Client: {client.ClientId}"); + + // + await configDbContext.Clients.AddAsync(client); + } + } + + // + // Save Changes on Config Db Context ... + await configDbContext.SaveChangesAsync(); + } + + // + logger.LogInformation($"Finish Seeding Clients ..."); + #endregion + + // + #region IdentityResources ... + // + logger.LogInformation($"Start Seeding Identity Resources ..."); + + // + var dIdentityResources = dbSeedDescriptor.IdentityResources; + var hasIdentityResources = configDbContext.IdentityResources.Any(); + if ((!hasIdentityResources || + (hasIdentityResources && dbSeedDescriptor.UpdateExists)) && + dIdentityResources.HasChild()) + { + // + var identityResourceEntities = dIdentityResources.Select(di => di.ToEntity()); + + // + foreach (var identityResource in identityResourceEntities) + { + // + logger.LogInformation($"Add/Update: {identityResource.DisplayName}"); + + // + // Retrieve Exists IdentityResource ... + var existsIdentityResource = configDbContext.IdentityResources + .AsNoTracking() + .FirstOrDefault(ir => ir.Name == identityResource.Name); + + // + var isExistsResource = !existsIdentityResource.IsNull(); + var isSameContent = isExistsResource && + existsIdentityResource.IsSameContent(identityResource, propertyBlackList: new[] { nameof(existsIdentityResource.Id) }); + + // + if (isExistsResource && + dbSeedDescriptor.UpdateExists) + { + // + if (!isSameContent) + { + // + logger.LogInformation($"Update IdentityResource: {identityResource.DisplayName}"); + + // + existsIdentityResource = existsIdentityResource.UpdateData(identityResource, propertyBlackList: new[] { nameof(existsIdentityResource.Id) }); + configDbContext.IdentityResources.Update(existsIdentityResource); + } + } + else + { + // + logger.LogInformation($"Add IdentityResource: {identityResource.DisplayName}"); + + // + await configDbContext.IdentityResources.AddAsync(identityResource); + } + } + + // + // Save Changes on Config Db Context ... + // await configDbContext.SaveChangesAsync(); + } + + // + logger.LogInformation($"Finish Seeding Identity Resources ..."); + #endregion + + // + #region Api Resources ... + // + logger.LogInformation($"Start Seeding Api Resources ..."); + + // + var dApiResources = dbSeedDescriptor.ApiResources; + var hasApiResources = configDbContext.ApiResources.Any(); + if ((!hasApiResources || + (hasApiResources && dbSeedDescriptor.UpdateExists)) && + dApiResources.HasChild()) + { + // + var apiResourceEntities = dApiResources.Select(da => da.ToEntity()); + + // + foreach (var apiResource in apiResourceEntities) + { + // + logger.LogInformation($"Add/Update: {apiResource.DisplayName}"); + + // + // Retrieve Exists ApiResource ... + var existsApiResource = configDbContext.ApiResources + .Include(r => r.Secrets) + .Include(r => r.Scopes) + .FirstOrDefault(ar => ar.Name == apiResource.Name); + + // + var isExistsResource = !existsApiResource.IsNull(); + var isSameContent = isExistsResource && + existsApiResource.IsSameContent(apiResource, propertyBlackList: new[] { nameof(existsApiResource.Id) }); + + // + if (isExistsResource && + dbSeedDescriptor.UpdateExists) + { + // + if (!isSameContent) + { + // + logger.LogInformation($"Update ApiResource: {existsApiResource.DisplayName}"); + + // + existsApiResource = existsApiResource.UpdateData(apiResource, propertyBlackList: new[] { nameof(existsApiResource.Id) }); + configDbContext.ApiResources.Update(existsApiResource); + } + } + else + { + // + logger.LogInformation($"Add ApiResource: {apiResource.DisplayName}"); + + // + await configDbContext.ApiResources.AddAsync(apiResource); + } + } + + // + // Save Changes on Config Db Context ... + // await configDbContext.SaveChangesAsync(); + } + + // + logger.LogInformation($"Finish Seeding Api Resources ..."); + #endregion + + // + #region Api Scopes ... + // + logger.LogInformation($"Start Seeding Scopes ..."); + + // + var dScopes = dbSeedDescriptor.ApiScopes; + var hasScope = configDbContext.ApiScopes.Any(); + if ((!hasScope || + (hasScope && dbSeedDescriptor.UpdateExists)) && + dScopes.HasChild()) + { + // + var apiScopeEntities = dScopes.Select(dc => dc.ToEntity()); + + // + foreach (var apiScope in apiScopeEntities) + { + // + logger.LogInformation($"Add/Update: {apiScope.DisplayName}"); + + // + // Retrieve Exists Client ... + var existsScope = configDbContext.ApiScopes + .FirstOrDefault(sc => sc.Name == apiScope.Name); + + // + var isExistsScope = !existsScope.IsNull(); + var isSameContent = isExistsScope && + existsScope.IsSameContent(apiScope, propertyBlackList: new[] { nameof(existsScope.Id) }); + + // + if (isExistsScope && + dbSeedDescriptor.UpdateExists) + { + // + if (!isSameContent) + { + // + logger.LogInformation($"Update ApiScope: {existsScope.Name}"); + + // + existsScope = existsScope.UpdateData(apiScope, propertyBlackList: new[] { nameof(existsScope.Id) }); + configDbContext.ApiScopes.Update(existsScope); + } + } + else + { + // + logger.LogInformation($"Add ApiScope: {apiScope.Name}"); + + // + await configDbContext.ApiScopes.AddAsync(apiScope); + } + } + + // + // Save Changes on Config Db Context ... + await configDbContext.SaveChangesAsync(); + } + + // + logger.LogInformation($"Finish Seeding Scopes ..."); + #endregion + + // + #region Users ... + // + logger.LogInformation($"Start Seeding Users ..."); + + // + var configUsers = dbSeedDescriptor.Users; + var hasUser = identityProvider.GetUsersDbSet().Any(); + if ((!hasUser || + (hasUser && dbSeedDescriptor.UpdateExists)) && + configUsers.HasChild()) + { + foreach (var user in configUsers) + { + // + logger.LogInformation($"Add/Update User: {user.UserName}"); + + // + var isUserExists = await identityProvider.IsUserExistsAsync(user.UserName); + if (isUserExists && + dbSeedDescriptor.UpdateExists) + { + // + var existsUser = await identityProvider.GetUserAsync(user.UserName); + var isSame = existsUser.IsSameAs(user); + if (!isSame) + { + // + // Update Entity ... + existsUser = existsUser.UpdateData(user, propertyWhiteList: new[] { + nameof (XIdentityUserDescriptor.FirstName), + nameof (XIdentityUserDescriptor.LastName), + nameof (XIdentityUserDescriptor.UserName), + nameof (XIdentityUserDescriptor.Email), + nameof (XIdentityUserDescriptor.EmailConfirmed), + nameof (XIdentityUserDescriptor.PhoneNumber), + nameof (XIdentityUserDescriptor.PhoneNumberConfirmed), + nameof (XIdentityUserDescriptor.IsEnable), + nameof (XIdentityUserDescriptor.IsBanned), + nameof (XIdentityUserDescriptor.DateOfBirth) + }, propertyValueProviders: new[] { + new KeyValuePair> ( + nameof (XIdentityUserDescriptor.DateOfBirth), + (user) => DateTime.Parse (user.DateOfBirth)) + }); + + // + await identityProvider.UpdateUserAsync(existsUser, false, false); + } + } + else + { + await identityProvider.CreateUserAsync(user); + } + } + } + + // + logger.LogInformation($"Finish Seeding Users ..."); + #endregion + } + + // + logger.LogInformation($"Finish Add/Update XIdentityServer Descriptors ..."); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Extensions\XModelExtensions.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using xCommons.Extensions; +using xDataService.Configuration; +using xIdentityModels.Models; + +namespace xIds.Extensions +{ + public static class XModelExtensions + { + /// + /// Determine a banned time is passed or not + /// + /// + /// + /// + public static bool IsDelayTimePassed( + this XBannedDevice source, + int delaySeconds + ) + { + // + var passedTime = source.BannedOn.AddSeconds(delaySeconds); + + // + var result = DateTime.UtcNow >= passedTime; + + // + return result; + } + + /// + /// Applying Filter to IQueryable + /// /// + /// + public static async Task> ApplyFilterAsync( + this IQueryable source, + string filter + ) where T : class + { + // + // Apply Filter ... + if (!filter.IsNullOrEmpty()) + { + // + var items = source.AsAsyncEnumerable(); + var filteredItems = new List(); + await + foreach (var item in items) + { + // + if (item.GetPropValues() + .ToNormalString() + .Contains(filter + .ToNormalString())) + { + // + filteredItems.Add(item); + } + } + + // + return filteredItems.AsQueryable(); + } + + // + return source; + } + + public static XDataServiceConfiguration ToXDataServiceConfig(this Configurations.XDataServiceConfiguration config) + { + // + var result = new XDataServiceConfiguration(); + + // + if (!config.IsNullOrDefault()) + { + // + result.PagingConfiguration.DefaultPageSize = config.PagingConfiguration.DefaultPageSize; + result.PagingConfiguration.MaxAvailablePageSize = config.PagingConfiguration.MaxAvailablePageSize; + result.PagingConfiguration.MinAvailablePageSize = config.PagingConfiguration.MinAvailablePageSize; + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Helpers\XIdentityHelper.cs + +using System; +using System.IdentityModel.Tokens.Jwt; +using Microsoft.IdentityModel.Tokens; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityModels.Configurations; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIds.Interfaces; + +namespace xIds.Helpers +{ + public class XIdentityHelper : IXIdentityHelper + { + private readonly XIdentityConfiguration configurations; + private readonly JwtSecurityTokenHandler tokenHandler; + private readonly XValidationProvider dataValidationHelper; + + public XIdentityHelper( + XIdentityConfiguration configurations, + XValidationProvider dataValidationHelper + ) + { + // + this.configurations = configurations; + this.tokenHandler = new JwtSecurityTokenHandler(); + this.dataValidationHelper = dataValidationHelper; + } + + // + #region Configuration Parser ... + /// + /// Generate a Key for Signing JWT Tokens + /// + /// + public SymmetricSecurityKey GetTokenSecretKey() + { + // + dataValidationHelper + .NotEmpty(configurations.IdentitySecretKey); + + // + var result = new SymmetricSecurityKey(configurations + .IdentitySecretKey.ToBytes()); + + // + return result; + } + + /// + /// Generate Security Token Signing Key + /// + /// + public SigningCredentials GetTokenSigningCredentials() + { + // + var key = GetTokenSecretKey(); + dataValidationHelper + .NotNull(key); + + // + var result = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + // + return result; + } + + /// + /// Get Token Validation Parameters + /// + /// + public TokenValidationParameters GetTokenValidationParams() + { + // + var key = GetTokenSecretKey(); + var issuer = configurations.IdentityIssuer; + var audience = configurations.IdentityAudience; + + // + dataValidationHelper + .NotNull(key); + dataValidationHelper + .NotEmpty(issuer, audience); + + // + var result = new TokenValidationParameters + { + ValidIssuer = issuer, + ValidAudience = audience, + ValidateLifetime = true, + IssuerSigningKey = key + }; + + // + return result; + } + + /// + /// Get Account Lockout Time Span after Max Fail Reached + /// + /// + public TimeSpan GetLockoutTimeSpan() + { + // + dataValidationHelper + .NotNull(configurations.Policy, configurations.Policy.Lockout); + + // + var timeSpanString = configurations.Policy.Lockout.LockoutTimeSpanProvider; + dataValidationHelper + .NotEmpty(timeSpanString); + + // + var mExpireValue = 0; + var mExpireValueStr = timeSpanString.GetDigits(); + var mExpireUnit = timeSpanString.Replace(mExpireValueStr, ""); + dataValidationHelper + .NotEmpty(mExpireUnit); + dataValidationHelper + .NotEmpty(mExpireValueStr); + + // + var isParsed = int.TryParse(mExpireValueStr, out mExpireValue); + + // + switch (mExpireUnit) + { + case "d": + return TimeSpan.FromDays(mExpireValue); + + case "h": + return TimeSpan.FromHours(mExpireValue); + + case "m": + return TimeSpan.FromMinutes(mExpireValue); + + case "s": + return TimeSpan.FromSeconds(mExpireValue); + + case "ms": + default: + return TimeSpan.FromMilliseconds(mExpireValue); + } + } + + /// + /// Generate Security Token Descriptor for Tokenize another + /// + /// + public SecurityTokenDescriptor GetActionTokenDescriptor() + { + // + dataValidationHelper + .NotNull(configurations); + + // + var issuer = configurations.IdentityIssuer; + var audience = configurations.IdentityAudience; + dataValidationHelper + .NotEmpty(issuer, audience); + + // + var expDate = GetActionTokenExpirationDate(); + var signingCreds = GetTokenSigningCredentials(); + dataValidationHelper + .NotNull(expDate, signingCreds); + + // + var descriptor = new SecurityTokenDescriptor + { + Issuer = issuer, + Audience = audience, + Expires = expDate, + SigningCredentials = signingCreds + }; + + // + return descriptor; + } + + /// + /// Get Action Token Expiration Date + /// + /// + public DateTime GetActionTokenExpirationDate() + { + // + dataValidationHelper + .NotNull(configurations); + + // + var expDateProvider = configurations + .ActionTokenExpirationDateProvider; + dataValidationHelper + .NotEmpty(expDateProvider); + + // + var result = GetExpirationTime(expDateProvider); + dataValidationHelper + .NotNull(result); + + // + return result; + } + #endregion + + // + #region Action Token Handlers ... + public bool ValidateToken(string token) + { + // + // Validate Args ... + dataValidationHelper + .NotEmpty(token); + dataValidationHelper + .NotNull(tokenHandler); + + // + // Prepare Requirements ... + var tokenValidationParams = GetTokenValidationParams(); + dataValidationHelper + .NotNull(tokenValidationParams); + + // + var result = token + .ValidateToken(tokenHandler, tokenValidationParams); + + // + return result; + } + + /// + /// Converts a Token string to SecurityToken instance + /// + /// + /// + public SecurityToken ToSecurityToken(string token) + { + // + dataValidationHelper + .NotEmpty(token); + + // + // Validate token ... + var isTokenValid = ValidateToken(token); + if (!isTokenValid) + { + XException.InvalidToken.Throw(); + } + + // + // Prepare and Validate Requirements ... + var tokenValidationParams = GetTokenValidationParams(); + dataValidationHelper + .NotNull(tokenHandler, tokenValidationParams); + + // + var result = token + .ToSecurityToken(tokenHandler, tokenValidationParams); + dataValidationHelper + .NotNull(result); + + // + return result; + } + + /// + /// Convert an ActionRequest to Token Object + /// + /// + /// + public SecurityToken ToSecurityToken(XActionRequestToken request) + { + // + var tokenDescriptor = GetActionTokenDescriptor(); + + // + dataValidationHelper.NotNull(request, tokenHandler, tokenDescriptor); + + // + var result = request.ToJwtToken(tokenHandler, tokenDescriptor); + + // + return result; + } + + /// + /// Retrieve a Token Exiration Date + /// + /// + /// + public DateTime GetTokenExpirationDate(string token) + { + // + var tokenObj = ToSecurityToken(token); + dataValidationHelper.NotNull(tokenObj); + + // + var result = tokenObj.ValidTo; + + // + return result; + } + + public XActionRequestToken ParseActionRequestToken(string token) + { + // + dataValidationHelper + .NotEmpty(token); + + // + // Validate token ... + var isTokenValid = ValidateToken(token); + if (!isTokenValid) + { + XException.InvalidToken.Throw(); + } + + // + // Prepare and Validate Requirements ... + var tokenValidationParams = GetTokenValidationParams(); + dataValidationHelper + .NotNull(tokenHandler, tokenValidationParams); + + // + var result = token + .ParseActionRequestToken(tokenHandler, tokenValidationParams); + dataValidationHelper + .NotNull(result); + + // + return result; + } + + /// + /// Convert a JWT Security Token Object to + /// it's String Representation + /// + /// + /// + public string ToTokenString(JwtSecurityToken token) + { + // + dataValidationHelper.NotNull(token); + + // + var result = tokenHandler + .WriteToken(token); + + // + return result; + } + + /// + /// Convert an Action Token Object to it's String Representation + /// + /// + /// + public string ToTokenString(SecurityToken token) + { + // + // Validate Args ... + dataValidationHelper + .NotNull(token, tokenHandler); + + // + // Generate and return Result ... + var result = token.ToTokenString(tokenHandler); + dataValidationHelper + .NotEmpty(result); + + // + return result; + } + #endregion + + // + #region Private Actions ... + /// + /// Calculating Date Difference Provider String + /// and Generate Date based on UTC Time + /// + /// + /// + private DateTime GetExpirationTime(string expirationProvider) + { + // + dataValidationHelper + .NotEmpty(expirationProvider); + + // + var mExpireValue = 0; + var mExpireValueStr = expirationProvider.GetDigits(); + var mExpireUnit = expirationProvider.Replace(mExpireValueStr, ""); + dataValidationHelper + .NotEmpty(mExpireUnit); + dataValidationHelper + .NotEmpty(mExpireValueStr); + + // + var isParsed = int.TryParse(mExpireValueStr, out mExpireValue); + + // + switch (mExpireUnit) + { + case "d": + return DateTime.UtcNow.AddDays(mExpireValue); + + case "h": + return DateTime.UtcNow.AddHours(mExpireValue); + + case "m": + return DateTime.UtcNow.AddMinutes(mExpireValue); + + case "s": + return DateTime.UtcNow.AddSeconds(mExpireValue); + + case "ms": + default: + return DateTime.UtcNow.AddMilliseconds(mExpireValue); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Interfaces\IXIdentityHelper.cs + +using System; +using System.IdentityModel.Tokens.Jwt; +using Microsoft.IdentityModel.Tokens; +using xIdentityModels.Models; + +namespace xIds.Interfaces +{ + public interface IXIdentityHelper + { + // + #region Configuration Parser ... + SymmetricSecurityKey GetTokenSecretKey(); + SigningCredentials GetTokenSigningCredentials(); + TokenValidationParameters GetTokenValidationParams(); + TimeSpan GetLockoutTimeSpan(); + #endregion + + // + #region Action Token Handlers ... + bool ValidateToken(string token); + SecurityToken ToSecurityToken(string token); + SecurityToken ToSecurityToken(XActionRequestToken request); + string ToTokenString(SecurityToken token); + string ToTokenString(JwtSecurityToken token); + DateTime GetTokenExpirationDate(string token); + XActionRequestToken ParseActionRequestToken(string token); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Interfaces\IXIdentityManager.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Security.Claims; +using System.Threading.Tasks; +using IdentityModel.Client; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using xCommons.Providers; +using xIdentityModels; +using xIdentityModels.Constants; +using xIdentityModels.Descriptors; +using xIdentityModels.Dtos; +using xIdentityModels.Models; +using xIdentityModels.Navigations; +using xIds.DbContext; +using xModels.Dtos; + +namespace xIds.Interfaces +{ + public interface IXIdentityManager + { + XIdentityDbContext DbContext { get; } + UserManager UserManager { get; } + ILogger Logger { get; } + SignInManager SignInManager { get; } + IXSecurityProvider SecurityProvider { get; } + RoleManager RoleManager { get; } + XValidationProvider ValidationProvider { get; } + + // + #region Class Getter (s) Methods ... + IQueryable GetUsersDbSet( + bool containsDetail = false + ); + IQueryable GetUsersFullDbSet(bool enableTracking = false); + #endregion + + // + #region Tools ... + string GetUserSelectByParam( + XActionRequest model, + bool forceNotNull = true, + ICollection excludes = null + ); + + Task ValidateUserExistsAndRetrieve( + string userSelectByParam, + bool containsDetail = false, + bool checkCanLoginPolicies = false, + bool checkIsBanned = true, + bool ignoreDisabledUser = false, + bool forceAdmin = false, + Exception exception = null + ); + #endregion + + // + #region Default Identity Preparation Actions ... + Task CreateIdentityRoles(); + Task CreateUserAsync(XIdentityUserDescriptor user); + #endregion + + // + #region Identity Actions ... + HttpClient GetHttpClient(); + Task RequestScopeAccessToken(string scope); + Task RequestDiscoveryDocument(); + Task Authenticate(XLoginRequest model); + Task Login(XLoginRequest model); + Task RefreshTokens(XTokenResponse model); + #endregion + + // + #region Role Handlers ... + Task IsRoleExistsAsync(string roleName); + Task CreateRoleAsync(string roleName); + Task GetRoleAsync(string roleName); + Task AddUserToRoleAsync(XUser user, string roleName); + Task GetRoleNameAsync(string roleId); + Task> GetRoleNamesAsync( + string userSelectByParam, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ); + Task RemoveFromRoleAsync(XUser user, string roleName); + Task RemoveFromRolesAsync(XUser user, IEnumerable roleNames); + #endregion + + // + #region User Handlers ... + Task CanRegister(string userSelectByParam); + Task IsUserExistsAsync(string userSelectByParam); + Task GetUserAsync( + string userSelectByParam, + bool containsDetail = false); + + Task CreateUserAsync( + XUser user, + string password, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ); + + Task UpdateUserAsync( + XUser user, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ); + + Task> GetUserNamesAsync( + IEnumerable userIds + ); + + Task> GetUserNameIdsAsync( + XUserNameIdRequest model + ); + + Task ToJwtClaims( + XUser user, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ); + + Task CheckPasswordSignInAsync( + XUser user, + string password, + XDevice device, + string language, + bool lockoutOnFailure, + bool isForced = true + ); + #endregion + + // + #region User Profile Handlers ... + Task GetUserProfileAsync( + string userSelectByParam, + string requestedUserSelectByParam, + bool forceCheckRequestedUser = true, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ); + + Task> QueryUsers( + string requestedUserSelectByParam, + XQuery query + ); + + Task> QueryInRoleUsers( + string requestedUserSelectByParam, + string role, + XQuery query, + bool forceRole = false + ); + + Task> GetUserProfilesAsync( + ICollection ids, + string requestedUserSelectByParam, + bool forceCheckRequestedUser = true, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ); + + Task> QueryAvatars( + string userSelectByParam, + XQuery query + ); + + Task ProfileUpdateAsync( + string userSelectByParam, + string requestedUserSelectByParam, + XProfileUpdateRequest request + ); + + Task FullProfileUpdateAsync( + string userSelectByParam, + string requestedUserSelectByParam, + XProfileUpdateRequest request + ); + + Task RequestConfirmMobile( + string lang, + XDevice device, + string userSelectByParam, + string password, + string mobileNumber = null + ); + + Task RequestConfirmEmail( + string lang, + XDevice device, + string userSelectByParam, + string password, + string emailAddress = null + ); + + Task RequestResetPassword( + string lang, + XDevice device, + string userSelectByParam, + string returnUrl + ); + + Task RequestConfirmRegistration( + string lang, + XDevice device, + string userSelectByParam, + string password, + string returnUrl + ); + + Task ChangePassword( + string lang, + XDevice device, + string userSelectByParam, + string password, + string newPassword, + string returnUrl + ); + + Task ResetPassword( + string lang, + XDevice device, + string actionHash, + string newPassword, + string returnUrl + ); + + Task AddAvatar( + string userSelectByParam, + IFormFile file + ); + + Task AddAvatars( + string userSelectByParam, + IFormFileCollection files + ); + + Task SetAvatar( + string userSelectByParam, + int profileImageId + ); + + Task RemoveAvatar( + string userSelectByParam, + ICollection profileImageIds + ); + + Task ConfirmRegistration( + string lang, + XDevice device, + string actionHash, + string returnUrl + ); + + Task ConfirmMobileNumber( + string lang, + XDevice device, + string registrationHash, + string verificationCode + ); + + Task ConfirmEmailAddress( + string lang, + XDevice device, + string registrationHash, + string verificationCode + ); + + Task IsConfirmedEmail(string userSelectByParam); + + Task IsConfirmedMobile(string userSelectByParam); + #endregion + + // + #region Registration Handlers ... + Task InviteUser( + string lang, + XDevice device, + string email, + string handlerUrl + ); + + Task RequestRegistration( + string lang, + XDevice device, + string invitationHash, + string firstName, + string lastName, + DateTime dateOfBirth, + string mobileNumber = null, + string emailAddress = null + ); + + Task AddAcountInfo( + string lang, + XDevice device, + string registrationHash, + string userName, + string password + ); + + Task AttachProfileImage( + string tokenHash, + IFormFile file + ); + + Task FinishRegistration( + string lang, + XDevice device, + string userSelectByParam, + string password, + string returnUrl + ); + #endregion + + // + #region Friendship Handlers ... + // + #region Actions ... + Task Follow( + string userSelectByParam, + string destUserSelectByParam + ); + + Task Cancel( + string userSelectByParam, + string destUserSelectByParam + ); + + Task UnFollowFollower( + string userSelectByParam, + string destUserSelectByParam + ); + + Task UnFollowFollowing( + string userSelectByParam, + string destUserSelectByParam + ); + + Task Block( + string userSelectByParam, + string destUserSelectByParam + ); + + Task UnBlock( + string userSelectByParam, + string destUserSelectByParam + ); + #endregion + + // + #region Request Handlers ... + Task AcceptRequest( + string userSelectByParam, + string destUserSelectByParam + ); + + Task RejectRequest( + string userSelectByParam, + string destUserSelectByParam + ); + #endregion + + // + #region Getters ... + Task IsFollower( + string userSelectByParam, + string destUserSelectByParam, + bool checkIsBanned = true, + bool checkCanLoginPolicies = true + ); + + Task GetFollowerState( + string userSelectByParam, + string destUserSelectByParam, + bool checkIsBanned = true, + bool checkCanLoginPolicies = true + ); + + Task GetFollower( + string userSelectByParam, + string destUserSelectByParam + ); + + Task> GetFollowers(string userSelectByParam); + Task> GetAllFollowers(string userSelectByParam); + Task> GetFollowersList(string userSelectByParam); + Task> QueryFollowers( + XQuery query, + string userSelectByParam + ); + + Task IsFollowing( + string userSelectByParam, + string destUserSelectByParam, + bool checkIsBanned = true, + bool checkCanLoginPolicies = true + ); + + Task GetFollowingState( + string userSelectByParam, + string destUserSelectByParam, + bool checkIsBanned = true, + bool checkCanLoginPolicies = true + ); + + Task GetFollowing( + string userSelectByParam, + string destUserSelectByParam + ); + + Task> GetFollowings(string userSelectByParam); + Task> GetAllFollowings(string userSelectByParam); + Task> GetFollowingList(string userSelectByParam); + Task> QueryFollowings( + XQuery query, + string userSelectByParam + ); + #endregion + + // + Task GetFriendshipInfo( + string userSelectByParam, + string destUserSelectByParam, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ); + #endregion + + // + #region Admin Actions ... + Task> Ban( + string userSelectByParam, + XUserNameIdRequest model + ); + + Task> UnBan( + string userSelectByParam, + XUserNameIdRequest model + ); + + Task IsBanned( + string userSelectByParam, + string destUserSelectByParam + ); + #endregion + + // + #region Open Actions ... + // + #region OpenGet ... + Task OpenGet( + XOpenActionInnerDto model + ); + Task OpenGet( + string token, + string actionRequest + ); + #endregion + + Task GetUserInfo( + XOpenActionRequestDto dto + ); + + Task> GetUserInfos( + XOpenActionRequestDto dto + ); + #endregion + + // + #region Search ... + /// + /// Query Open To Search User Profiles ... + /// + /// requested user's identifier + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XUserProfileDto + Task> QueryOpenToSearchUsers( + string requestedUserSelectByParam, + XQuery query + ); + #endregion + + // + #region Query Service ... + /// + /// Retrieve Users as Query Model for Query Service ... + /// + /// + /// + /// + /// + /// + public Task> QueryUsers( + string requestedUserSelectByParam, + XQuery query, + string role = null, + bool forceRole = false + ); + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Interfaces\IXIdentityMessageProvider.cs + +namespace xIds.Interfaces +{ + public interface IXIdentityMessageProvider + { + string InviteMsg { get; } + string RegistrationApproveMsg { get; } + string RegisteredMsg { get; } + string VerificationCodeMsg { get; } + string ChangePasswordMsg { get; } + string PasswordChangedMsg { get; } + string NewDeviceLoggedInMsg { get; } + + bool IsReady(); + void PrepareMessages(string lang); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Interfaces\IXSecurityProvider.cs + +namespace xIds.Interfaces +{ + public interface IXSecurityProvider + { + string Encrypt(string plainText); + string Decrypt(string cipherText); + string EncryptFromBytes(byte[] textBytes); + string DecryptFromBytes(byte[] cipherBytes); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Models\XDbInfo.cs + +using System; +using xIds.Constants; + +namespace xIds.Models +{ + public class XDbInfo + { + private string _migrationAssembly; + public XDbProviders ProviderType { get; set; } + public string MigrationsAssembly + { + get + { + return _migrationAssembly; + } + + set + { + // + _migrationAssembly = value; + + // + OptionsBuilder = b => + { + b.MigrationsAssembly(value); + }; + } + } + public Action OptionsBuilder { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Models\XDbSeedDescriptor.cs + +using System.Collections.Generic; +using IdentityServer4.Models; +using xIdentityModels.Descriptors; + +namespace xIds.Models +{ + /// + /// the resource Descriptors for Using on DbSeed + /// + public class XDbSeedDescriptor + { + public bool UpdateExists { get; set; } + public ICollection ApiScopes { get; set; } + public ICollection ApiResources { get; set; } + public ICollection IdentityResources { get; set; } + public ICollection Clients { get; set; } + public ICollection Users { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Admin.cs + +using System.Collections.Generic; +using System.Threading.Tasks; +using xCommons.Extensions; +using xIdentityModels.Dtos; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + #region Admin Actions ... + /// + /// Retrieve All UnBanned Users Identifiers + /// + /// caller user identifier + /// a collection of user identifiers + public async Task> GetAllUnbanned( + ICollection userSelectByParam + ) + { + // + var result = new List(); + if (!userSelectByParam.HasChild()) + { + return result; + } + + // + foreach (var uParam in userSelectByParam) + { + // + var destUser = await ValidateUserExistsAndRetrieve(uParam, checkIsBanned: false); + if (!destUser.IsBanned) + { + result.Add(uParam); + } + } + + // + return result; + } + + /// + /// Get All Banned Users Identifiers + /// + /// caller user identifier + /// a collection of user identifiers + public async Task> GetAllBanned( + ICollection userSelectByParam + ) + { + // + var result = new List(); + if (!userSelectByParam.HasChild()) + { + return result; + } + + // + foreach (var uParam in userSelectByParam) + { + // + var destUser = await ValidateUserExistsAndRetrieve(uParam, checkIsBanned: false); + if (destUser.IsBanned) + { + result.Add(uParam); + } + } + + // + return result; + } + + /// + /// Ban a Collection of Users + /// + /// caller user identifier + /// an instance of XUserNameIdRequest which represents user identifier list to Ban + /// a collection of user identifiers + public async Task> Ban( + string userSelectByParam, + XUserNameIdRequest model + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + ValidationProvider.NotNull(model); + ValidationProvider.NotZeroChilds(model.Ids); + + // + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: true, + checkIsBanned: true, + containDetails: false, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + var result = new List(); + foreach (var uParam in model.Ids) + { + // + var destUser = await ValidateUserExistsAndRetrieve(uParam, checkIsBanned: false); + + // + var isBanned = destUser.IsBanned; + if (!isBanned) + { + // + destUser.IsBanned = true; + + // + await UpdateUserAsync( + user, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + result.Add(destUser.Id); + } + } + + // + return result; + } + + /// + /// UnBan a Collection of Users + /// + /// caller user identifier + /// an instance of XUserNameIdRequest which represents user identifier list to Ban + /// a collection of user identifiers + public async Task> UnBan( + string userSelectByParam, + XUserNameIdRequest model + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + ValidationProvider.NotNull(model); + ValidationProvider.NotZeroChilds(model.Ids); + + // + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: true, + checkIsBanned: true, + containDetails: false, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + var result = new List(); + foreach (var uParam in model.Ids) + { + // + var destUser = await ValidateUserExistsAndRetrieve(uParam, checkIsBanned: false); + + // + var isBanned = destUser.IsBanned; + if (isBanned) + { + // + destUser.IsBanned = false; + + // + await UpdateUserAsync( + user, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + result.Add(destUser.Id); + } + } + + // + return result; + } + + /// + /// Detrmines a User is Banned or not + /// + /// caller user identifier + /// destination user identifier which checked is banned or not + /// a boolean value which represent user banned or not + public async Task IsBanned( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: false, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + var destUser = await ValidateUserExistsAndRetrieve(destUserSelectByParam, checkIsBanned: false); + + // + var result = destUser.IsBanned; + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Device.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIdentityModels.Navigations; +using xIds.Extensions; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Device Actions ... + /// + /// Check a Device Exists or Not + /// + /// an instance of XDevice + /// a boolean value + public async Task IsDeviceExistsAsync( + XDevice device + ) + { + return await DbContext.Devices + .AnyAsync(d => d.IsSameAs(device)); + } + + /// + /// Add new Device + /// + /// an instance of XDevice + /// an instance of XDevice + public async Task AddDeviceAsync( + XDevice device + ) + { + // + var isDeviceExists = await IsDeviceExistsAsync(device); + if (isDeviceExists) + { + return null; + } + + // + await DbContext.Devices + .AddAsync(device); + + await DbContext.SaveChangesAsync(); + + return device; + } + + /// + /// Get a Device + /// + /// an instance of XDevice + /// an instance of XDevice + public Task GetDeviceAsync( + XDevice device + ) + { + // + return DbContext.Devices + .FirstOrDefaultAsync(d => + d.IsSameAs(device)); + } + + /// + /// Retrieve a List of User Related Devices + /// + /// user identifier + /// check a user can log in in system or not + /// check user is banned or not + /// a collection of XDevice instances which related to user + public async Task> GetUserDevices( + string userSelectByParam, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ) + { + // + // Validate ARgs ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Validate And Retrieve User ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: checkCanLoginPolicies, + checkIsBanned: checkIsBanned + ); + + // + // Check User Devices ... + if (!user.Devices.HasChild()) + { + return new List(); + } + + // + return user.Devices; + } + + /// + /// Determines a Device is Exists in User's Devices or not + /// + /// an instance of XUser + /// an instance of XDevice + /// a boolean value + public bool IsDeviceRelateDToUser( + XUser user, + XDevice device + ) + { + if (!user.Devices.HasChild()) + { + return false; + } + + // + var result = user.Devices.Any(d => d.IsSameAs(device)); + + // + return result; + } + + /// + /// Determines a Device is Exists in User's Devices or not + /// + /// user identifier + /// an instance of XDevice + /// check a user can log in in system or not + /// check user is banned or not + /// a boolean value + public async Task IsDeviceRelateDToUser( + string userSelectByParam, + XDevice device, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + ValidationProvider.NotNull(device); + + // + var userDevices = await GetUserDevices( + userSelectByParam, + checkCanLoginPolicies: checkCanLoginPolicies, + checkIsBanned: checkIsBanned + ); + if (!userDevices.HasChild()) + { + return false; + } + + // + var result = userDevices.Any(d => d.IsSameAs(device)); + + // + return result; + } + + /// + /// Add a Device to a User Related Devices + /// + /// an instance of XUser + /// an instance of XDevice + /// a boolean value + public async Task AddUserRelatedDevice( + XUser user, + XDevice device + ) + { + // + var isExists = user.Devices.Any(d => + d.IsSameAs(device)); + if (isExists) + { + return true; + } + + // + user.Devices.Add(device); + try + { + var result = await UpdateUserAsync(user); + return result.Succeeded; + } + catch + { + throw XException.ActionFailed.ToException(); + } + } + + /// + /// Add a Device to a User + /// + /// user identifier + /// an instance of XDevice + /// check a user can log in in system or not + /// check user is banned or not + /// a boolean value + public async Task AddUserRelatedDevice( + string userSelectByParam, + XDevice device, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + ValidationProvider.NotNull(device); + + // + // Check Device Related ... + var isDeviceRelatedToUser = await IsDeviceRelateDToUser( + userSelectByParam, + device, + checkCanLoginPolicies: checkCanLoginPolicies, + checkIsBanned: checkIsBanned); + if (isDeviceRelatedToUser) + { + return true; + } + + // + // Retrieve User ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: checkCanLoginPolicies, + checkIsBanned: checkIsBanned + ); + + // + // Add User Device ... + user.Devices.Add(device); + + // + var result = await UpdateUserAsync( + user, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + return result.Succeeded; + } + + /// + /// Remove a Device from a User Related Devices List + /// + /// an instance of XUser + /// an instance of XDevice + /// a boolean value + public async Task RemoveUserRelatedDevice( + XUser user, + XDevice device + ) + { + // + var isRelated = IsDeviceRelateDToUser(user, device); + if (!isRelated) + { + return false; + } + + // + var instanse = user.Devices + .FirstOrDefault(d => + d.IsSameAs(device)); + + // + user.Devices.Remove(instanse); + + // + var result = await UpdateUserAsync( + user, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + return result.Succeeded; + } + + /// + /// Remove a Device from a User Devices + /// + /// user identifier + /// an instance of XDevice + /// check a user can log in in system or not + /// check user is banned or not + /// a boolean value + public async Task RemoveUserRelatedDevice( + string userSelectByParam, + XDevice device, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + ValidationProvider.NotNull(device); + + // + // Validate user exists and Retrieve it based on userselectbyparam ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: checkCanLoginPolicies, + checkIsBanned: checkIsBanned + ); + + // + return await RemoveUserRelatedDevice( + user, + device + ); + } + #endregion + + // + #region Banned Device Actions ... + /// + /// Check a Device is exists in Banned Devices or not + /// + /// an instance of XDevice + /// a boolean value + public async Task IsBannedDeviceExists( + XDevice device + ) + { + // + var result = false; + var bannedDeviceEnumerable = DbContext.BannedDevices.AsAsyncEnumerable(); + await + foreach (var bannedDevice in bannedDeviceEnumerable) + { + // + if (bannedDevice.Device.IsSameAs(device)) + { + // + result = true; + break; + } + } + + // + return result; + } + + /// + /// Find First Banned Device + /// + /// an instance of XDevice + /// an instance of XDevice + public async Task BannedDeviceFindOne( + XDevice device + ) + { + // + var result = await DbContext.BannedDevices + .FirstOrDefaultAsync(bd => + bd.Device.IsSameAs(device) + ); + + // + return result; + } + + /// + /// Remove Banned Device + /// + /// an instance of XBannedDevice + /// + public async Task BannedDeviceRemove( + XBannedDevice model + ) + { + // + var isExists = await IsBannedDeviceExists(model.Device); + if (!isExists) + { + return; + } + + // + var item = await BannedDeviceFindOne(model.Device); + if (item == null) + { + return; + } + + // + DbContext.BannedDevices.Remove(item); + await DbContext.SaveChangesAsync(); + } + + /// + /// Check a Device is Banned or not + /// + /// an instance of XDevice + /// a boolean value + private async Task IsDeviceBanned( + XDevice device + ) + { + // + var result = await IsBannedDeviceExists(device); + return result; + } + + /// + /// Retrieve Specific Banned Device + /// + /// an instance of XDevice + /// an instance of XBannedDevice + private async Task GetBannedDevice( + XDevice device + ) + { + // + var result = await BannedDeviceFindOne(device); + return result; + } + + /// + /// Is a Banned Device passed Banning Time + /// + /// an instance of XBannedDevice + /// a boolean value + private bool IsDelayTimePassed( + XBannedDevice bannedDevice + ) + { + // + // Validate Args ... + if (bannedDevice == null || + Configuration == null) + { + XException.InvalidArgs.Throw(); + } + + // + var isPassed = bannedDevice + .IsDelayTimePassed(Configuration.BannedDeviceTimeout); + + // + return isPassed; + } + + /// + /// Retrieve Passed Time from Banning Time of specific Device + /// + /// an instance of XBannedDevice + /// an instance of DateTime which represent Passed Time + private DateTime GetPassedTime( + XBannedDevice bannedDevice + ) + { + // + var result = bannedDevice.BannedOn + .AddSeconds(Configuration.BannedDeviceTimeout); + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Friendship.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using xCommons.Extensions; +using xDataService.Extensions; +using xExceptions.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Dtos; +using xIdentityModels.Navigations; +using xModels.Dtos; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Friendship Actions ... + // + #region Actions ... + /// + /// Send a Follow Request for userSelectByParam to + /// destUserSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// an instance of XFriendshipFollowing + public async Task Follow( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + XFriendshipFollowing following = null; + XFriendshipFollower follower = null; + var isRequested = IsFollowRequested(xUser, xDestUser); + // + // Check Request Happens Or not ... + if (!isRequested) + { + // + following = new XFriendshipFollowing + { + UserId = xUser.Id, + DestId = xDestUser.Id, + State = XFriendshipState.Pending + }; + + // + follower = new XFriendshipFollower + { + UserId = xDestUser.Id, + DestId = xUser.Id, + State = XFriendshipState.Pending + }; + + // + xUser.Followings.Add(following); + xDestUser.Followers.Add(follower); + } + else + { + // + // if Request Done Before Must Check State ... + following = xUser.Followings.FirstOrDefault(f => f.DestId == xDestUser.Id); + follower = xDestUser.Followers.FirstOrDefault(f => f.DestId == xUser.Id); + if (following.IsNull() || + follower.IsNull()) + { + XException.NotFound.Throw(); + } + + // + switch (following.State) + { + case XFriendshipState.Accepted: + case XFriendshipState.Blocked: + throw XException.NotAllowed.ToException(); + + case XFriendshipState.Rejected: + following.State = XFriendshipState.Pending; + follower.State = XFriendshipState.Pending; + break; + } + } + + // + var canContinue = !following.IsNull() && !follower.IsNull(); + if (!canContinue) + { + XException.ActionFailed.Throw(); + } + + // + await UpdateUserAsync(xUser); + await UpdateUserAsync(xDestUser); + + // + var result = following; + + // + return result; + } + + /// + /// Cancel Following + /// + /// caller user identifier + /// dest user identifier + /// a boolean value + public async Task Cancel( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + XFriendshipFollowing following = null; + XFriendshipFollower follower = null; + var isRequested = IsFollowRequested(xUser, xDestUser); + // + // Check Request Happens Or not ... + if (!isRequested) + { + return false; + } + + // + // if Request Done Before Must Check State ... + following = xUser.Followings.FirstOrDefault(f => f.DestId == xDestUser.Id); + follower = xDestUser.Followers.FirstOrDefault(f => f.DestId == xUser.Id); + if (following.IsNull() || + follower.IsNull()) + { + XException.NotFound.Throw(); + } + + // + switch (following.State) + { + case XFriendshipState.Accepted: + case XFriendshipState.Blocked: + throw XException.NotAllowed.ToException(); + + case XFriendshipState.Rejected: + case XFriendshipState.Pending: + DbContext.Followers.Remove(follower); + DbContext.Followings.Remove(following); + break; + } + + // + var canContinue = !following.IsNull() && !follower.IsNull(); + if (!canContinue) + { + XException.ActionFailed.Throw(); + } + + // + await DbContext.SaveChangesAsync(); + + // + return true; + } + + /// + /// userSelectByParam remove friendship by it's Follower + /// destUserSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// + public async Task UnFollowFollower( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + ValidateIsFollowers(xUser, xDestUser); + + // + var follower = xUser.Followers + .FirstOrDefault(f => + f.DestId == xDestUser.Id && + f.State == XFriendshipState.Accepted); + var following = xDestUser.Followings + .FirstOrDefault(f => + f.DestId == xUser.Id && + f.State == XFriendshipState.Accepted); + if (follower.IsNull() || + following.IsNull()) + { + XException.NotFound.Throw(); + } + + // + DbContext.Followers.Remove(follower); + DbContext.Followings.Remove(following); + + // + await DbContext.SaveChangesAsync(); + } + + /// + /// userSelectByParam remove friendship by it's Followings + /// destUserSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// + public async Task UnFollowFollowing( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + ValidateIsFollowings(xUser, xDestUser); + + // + var following = xUser.Followings + .FirstOrDefault(f => + f.DestId == xDestUser.Id && + f.State == XFriendshipState.Accepted); + var follower = xDestUser.Followers + .FirstOrDefault(f => + f.DestId == xUser.Id && + f.State == XFriendshipState.Accepted); + if (follower.IsNull() || + following.IsNull()) + { + XException.NotAllowed.Throw(); + } + + // + DbContext.Followers.Remove(follower); + DbContext.Followings.Remove(following); + + // + await DbContext.SaveChangesAsync(); + } + + /// + /// userSelectByParam Block it's follower destUserSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// an instance of XFriendshipFollowing + public async Task Block( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + // Validate XDestUser is Accepted Follower of XUser ... + ValidateIsFollowers(xUser, xDestUser); + + // + // Retrieve FriendShip Models ... + var following = xDestUser.Followings + .FirstOrDefault(f => + f.DestId == xUser.Id && + ( + f.State == XFriendshipState.Accepted + ) + ); + var follower = xUser.Followers + .FirstOrDefault(f => + f.DestId == xDestUser.Id && + ( + f.State == XFriendshipState.Accepted + ) + ); + if (following.IsNull() || + follower.IsNull()) + { + XException.NotAllowed.Throw(); + } + + // + follower.State = XFriendshipState.Blocked; + following.State = XFriendshipState.Blocked; + + // + DbContext.Followers.Update(follower); + DbContext.Followings.Update(following); + + // + await DbContext.SaveChangesAsync(); + + // + var result = following; + + // + return result; + } + + /// + /// userSelectByParam UnBlock it's follower destUserSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// an instance of XFriendshipFollowing + public async Task UnBlock( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + // Validate XDestUser is Accepted Follower of XUser ... + ValidateIsBlock(xUser, xDestUser); + + // + // Retrieve FriendShip Models ... + var following = xDestUser.Followings + .FirstOrDefault(f => + f.DestId == xUser.Id && + ( + f.State == XFriendshipState.Blocked + ) + ); + var follower = xUser.Followers + .FirstOrDefault(f => + f.DestId == xDestUser.Id && + ( + f.State == XFriendshipState.Blocked + ) + ); + if ( + following.IsNull() || + follower.IsNull() + ) + { + XException.NotAllowed.Throw(); + } + + // + follower.State = XFriendshipState.Accepted; + following.State = XFriendshipState.Accepted; + + // + DbContext.Followers.Update(follower); + DbContext.Followings.Update(following); + + // + await DbContext.SaveChangesAsync(); + + // + var result = following; + + // + return result; + } + #endregion + + // + #region Request Handlers ... + /// + /// userSelectByParam Accept Follow Request of + /// destUserSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// an instance of XFriendshipFollower + public async Task AcceptRequest( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + // Validate Request Sent and in Pending State ... + ValidateFollowingRequestForAccept(xUser, xDestUser); + + // + // Retrieve FriendShip Models ... + var following = xDestUser.Followings + .FirstOrDefault(f => + f.DestId == xUser.Id && + ( + f.State == XFriendshipState.Pending || + f.State == XFriendshipState.Rejected + ) + ); + var follower = xUser.Followers + .FirstOrDefault(f => + f.DestId == xDestUser.Id && + ( + f.State == XFriendshipState.Pending || + f.State == XFriendshipState.Rejected + ) + ); + if (following.IsNull() || + follower.IsNull()) + { + XException.NotAllowed.Throw(); + } + + // + follower.State = XFriendshipState.Accepted; + following.State = XFriendshipState.Accepted; + + // + await UpdateUserAsync(xUser); + await UpdateUserAsync(xDestUser); + + // + var result = follower; + + // + return result; + } + + /// + /// Reject a Follow Request of destUserSelectByParam + /// with userSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// an instance of XFriendshipFollower + public async Task RejectRequest( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + // Validate Request Sent and in Pending State ... + ValidateFollowingRequest(xUser, xDestUser); + + // + // Retrieve Pending Specified User Request Friendship models ... + var following = xDestUser.Followings + .FirstOrDefault(f => + f.DestId == xUser.Id && + f.State == XFriendshipState.Pending); + var follower = xUser.Followers + .FirstOrDefault(f => + f.DestId == xDestUser.Id && + f.State == XFriendshipState.Pending); + if (following.IsNull() || + follower.IsNull()) + { + XException.NotFound.Throw(); + } + + // + follower.State = XFriendshipState.Rejected; + following.State = XFriendshipState.Rejected; + + // + DbContext.Followers.Update(follower); + DbContext.Followings.Update(following); + + // + await DbContext.SaveChangesAsync(); + + // + var result = follower; + + // + return result; + } + #endregion + + // + #region Getters ... + /// + /// determines destUserSelectByParam is in followers of + /// userSelectByParam or not + /// + /// caller user identifier + /// dest user identifier + /// check user is banned or not, default is true + /// check a user can log in in system or not, default is true + /// a boolean value + public async Task IsFollower( + string userSelectByParam, + string destUserSelectByParam, + bool checkIsBanned = true, + bool checkCanLoginPolicies = true + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + containDetails: true, + ignoreDisabledUser: false, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + containDetails: true, + ignoreDisabledUser: false, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + var result = IsFollowRequested(xDestUser, xUser, XFriendshipState.Accepted); + + // + return result; + } + + /// + /// retrieve Friendship state of destUserSelectByParam + /// in Followers of userSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// check user is banned or not, default is true + /// check a user can log in in system or not, default is true + /// a member of XFriendshipState + public async Task GetFollowerState( + string userSelectByParam, + string destUserSelectByParam, + bool checkIsBanned = true, + bool checkCanLoginPolicies = true + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + containDetails: true, + ignoreDisabledUser: false, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + containDetails: true, + ignoreDisabledUser: false, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + var isFollow = IsFollowRequested(xDestUser, xUser); + if (!isFollow) + { + return XFriendshipState.None; + } + + // + var follower = xUser.Followers.FirstOrDefault(f => f.DestId == xDestUser.Id); + if (follower.IsNull()) + { + XException.NotFound.Throw(); + } + + // + return follower.State; + } + + /// + /// get follower friendship model of destUserSelectByParam + /// in followers of userSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// an instance of XFriendshipFollower + public async Task GetFollower( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + var isFollow = IsFollowRequested(xDestUser, xUser); + if (!isFollow) + { + return null; + } + + // + var follower = xUser.Followers + .FirstOrDefault(f => f.DestId == xDestUser.Id); + if (follower.IsNull()) + { + XException.NotFound.Throw(); + } + + // + var result = follower; + + // + return result; + } + + /// + /// Retrieve Accepted Followers list of userSelectByParam + /// + /// caller user identifier + /// a collection of XFriendshipFollower instances + public async Task> GetFollowers( + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + var result = xUser.Followers + .Where(f => f.State == XFriendshipState.Accepted); + + // + return result; + } + + /// + /// Retrieve all Followers list of userSelectByParam + /// + /// caller user identifier + /// a collection of XFriendshipFollower instances + public async Task> GetAllFollowers( + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + var result = xUser.Followers; + + // + return result; + } + + /// + /// Read Specified User's Follower's List based on Query Model ... + /// + /// + /// + /// + public async Task> QueryFollowers( + XQuery query, + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(query); + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: false, + containDetails: false, + ignoreDisabledUser: false, + checkCanLoginPolicies: false + ); + + // + // Prepare Query ... + + // + if (query.PageSize < DataConfiguration + .PagingConfiguration + .MinAvailablePageSize) + { + // + query.PageSize = DataConfiguration + .PagingConfiguration + .DefaultPageSize; + } + + // + if (query.PageSize > DataConfiguration + .PagingConfiguration + .MaxAvailablePageSize) + { + // + query.PageSize = DataConfiguration + .PagingConfiguration + .MaxAvailablePageSize; + } + + // + // Prepare Enumerator ... + Expression> whereClause = x => x.UserId == xUser.Id; + var enumerator = DbContext.Followers.AsAsyncEnumerable(); + var resultItems = new List(); + await foreach (var entity in enumerator) + { + // + var isApproved = whereClause.Compile()(entity); + if (isApproved) + { + // + // Convert Approved Entity to Dto ... + // then Validate it and Add it to Results ... + var dto = await ToDto(entity); + if (!dto.IsNull()) + { + resultItems.Add(dto); + } + } + } + + // + // Enumerable ... + var result = resultItems.AsEnumerable(); + + // + // Count Total Items ... + var totalItemsCount = result.Count(); + + // + // Apply Filter ... + if (!query.Filter.IsNullOrEmpty()) + { + result = result.ApplyFilter(query.Filter); + } + + // + // Count Filtered Items ... + var totalFilteredItemsCount = result.Count(); + + // + // Apply Paging ... + if (totalItemsCount > 0 && + totalFilteredItemsCount > 0) + { + // + // Apply Sorting ... + if (!query.SortBy.IsNullOrEmpty()) + { + // + // Since Sorting Based on Date is Very Important ... + // we have to do Sorting at first Time then + // try to apply Paging ... + result = result + .ApplySorting( + query.SortBy, + query.IsAscending) + .ToList(); + } + + // + // Apply Paging ... + result = result + .ApplyPaging( + query.Page, + query.PageSize) + .ToList(); + } + + // + // Calculating Page Count ... + int pagesCount = totalItemsCount / query.PageSize; + if (totalItemsCount % query.PageSize > 0) + { + pagesCount++; + } + + // + // Generate Result Object ... + var queryResult = new XQueryResult + { + Page = query.Page, + TotalPages = pagesCount, + PageSize = query.PageSize, + TotalItems = totalItemsCount, + Items = result.AsEnumerable(), + TotalFilteredItems = totalFilteredItemsCount + }; + + // + return queryResult; + } + + /// + /// determines destUserSelectByParam is in followings of + /// userSelectByParam or not + /// + /// caller user identifier + /// dest user identifier + /// check user is banned or not, default is true + /// check a user can log in in system or not, default is true + /// a boolean value + public async Task IsFollowing( + string userSelectByParam, + string destUserSelectByParam, + bool checkIsBanned = true, + bool checkCanLoginPolicies = true + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + containDetails: true, + ignoreDisabledUser: false, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + containDetails: true, + ignoreDisabledUser: false, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + var result = IsFollowRequested(xUser, xDestUser, XFriendshipState.Accepted); + + // + return result; + } + + /// + /// retrieve Frindship State of destUserSelectByParam + /// in Followings of userSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// check user is banned or not, default is true + /// check a user can log in in system or not, default is true + /// a member of XFriendshipState + public async Task GetFollowingState( + string userSelectByParam, + string destUserSelectByParam, + bool checkIsBanned = true, + bool checkCanLoginPolicies = true + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + containDetails: true, + ignoreDisabledUser: false, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + containDetails: true, + ignoreDisabledUser: false, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + var isFollow = IsFollowRequested(xUser, xDestUser); + if (!isFollow) + { + return XFriendshipState.None; + } + + // + var following = xUser.Followings.FirstOrDefault(f => f.DestId == xDestUser.Id); + if (following.IsNull()) + { + XException.NotFound.Throw(); + } + + // + return following.State; + } + + /// + /// get following friendship model of destUserSelectByParam + /// in followings of userSelectByParam + /// + /// caller user identifier + /// dest user identifier + /// an instance of XFriendshipFollowing + public async Task GetFollowing( + string userSelectByParam, + string destUserSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + // Validate Not Same Source and Dest Users ... + ValidateNotSameUsers(xUser, xDestUser); + + // + var isFollow = IsFollowRequested(xUser, xDestUser); + if (!isFollow) + { + return null; + } + + // + var following = xUser.Followings.FirstOrDefault(f => f.DestId == xDestUser.Id); + if (following.IsNull()) + { + XException.NotFound.Throw(); + } + + // + var result = following; + + // + return result; + } + + /// + /// Retrieve accepted Followings List of userSelectByParam + /// + /// caller user identifier + /// a collection of XFriendshipFollowing instances + public async Task> GetFollowings( + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + var result = xUser.Followings + .Where(f => f.State == XFriendshipState.Accepted); + + // + return result; + } + + /// + /// Retrieve all Followings List of userSelectByParam + /// + /// caller user identifier + /// a collection of XFriendshipFollowing instances + public async Task> GetAllFollowings(string userSelectByParam) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: true, + containDetails: true, + ignoreDisabledUser: false, + checkCanLoginPolicies: true + ); + + // + var result = xUser.Followings; + + // + return result; + } + + /// + /// Read Specified User's Following's List based on Query Model ... + /// + /// + /// + /// + public async Task> QueryFollowings( + XQuery query, + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(query); + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Validate and Retrieve User ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + checkIsBanned: false, + containDetails: false, + ignoreDisabledUser: false, + checkCanLoginPolicies: false + ); + + // + // Prepare Query ... + + // + if (query.PageSize < DataConfiguration + .PagingConfiguration + .MinAvailablePageSize) + { + // + query.PageSize = DataConfiguration + .PagingConfiguration + .DefaultPageSize; + } + + // + if (query.PageSize > DataConfiguration + .PagingConfiguration + .MaxAvailablePageSize) + { + // + query.PageSize = DataConfiguration + .PagingConfiguration + .MaxAvailablePageSize; + } + + // + // Prepare Enumerator ... + Expression> whereClause = x => x.UserId == xUser.Id; + var enumerator = DbContext.Followings.AsAsyncEnumerable(); + var resultItems = new List(); + await foreach (var entity in enumerator) + { + // + var isApproved = whereClause.Compile()(entity); + if (isApproved) + { + // + // Convert Approved Entity to Dto ... + // then Validate it and Add it to Results ... + var dto = await ToDto(entity); + if (!dto.IsNull()) + { + resultItems.Add(dto); + } + } + } + + // + // Enumerable ... + var result = resultItems.AsEnumerable(); + + // + // Count Total Items ... + var totalItemsCount = result.Count(); + + // + // Apply Filter ... + if (!query.Filter.IsNullOrEmpty()) + { + result = result.ApplyFilter(query.Filter); + } + + // + // Count Filtered Items ... + var totalFilteredItemsCount = result.Count(); + + // + // Apply Paging ... + if (totalItemsCount > 0 && + totalFilteredItemsCount > 0) + { + // + // Apply Sorting ... + if (!query.SortBy.IsNullOrEmpty()) + { + // + // Since Sorting Based on Date is Very Important ... + // we have to do Sorting at first Time then + // try to apply Paging ... + result = result + .ApplySorting( + query.SortBy, + query.IsAscending) + .ToList(); + } + + // + // Apply Paging ... + result = result + .ApplyPaging( + query.Page, + query.PageSize) + .ToList(); + } + + // + // Calculating Page Count ... + int pagesCount = totalItemsCount / query.PageSize; + if (totalItemsCount % query.PageSize > 0) + { + pagesCount++; + } + + // + // Generate Result Object ... + var queryResult = new XQueryResult + { + Page = query.Page, + TotalPages = pagesCount, + PageSize = query.PageSize, + TotalItems = totalItemsCount, + Items = result.AsEnumerable(), + TotalFilteredItems = totalFilteredItemsCount + }; + + // + return queryResult; + } + #endregion + + // + #region Others ... + /// + /// Retrieve List Of All Followings User Id's + /// of userSelectByParam + /// + /// caller user identifier + /// a collection of user identifiers + public async Task> GetFollowingList( + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Validate and Retrieve followings List ... + var followings = await GetFollowings(userSelectByParam); + + // + var result = followings.Select(f => f.DestId); + + // + return result.ToList(); + } + + /// + /// Retrieve List Of All Followers User Id's + /// of userSelectByParam + /// + /// caller user identifier + /// a collection of user identifiers + public async Task> GetFollowersList( + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Validate and Retrieve followings List ... + var followers = await GetFollowers(userSelectByParam); + + // + var result = followers.Select(f => f.DestId); + + // + return result.ToList(); + } + + /// + /// retrieve Friendship Info Model for Specific User + /// + /// caller user identifier + /// dest user identifier + /// check a user can log in in system or not, default is true + /// check user is banned or not, default is true + /// an instance of XFriendshipInfoDto + public async Task GetFriendshipInfo( + string userSelectByParam, + string destUserSelectByParam, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + destUserSelectByParam + ); + + // + // Validate and Retrieve Dest User ... + var xSourceUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + forceAdmin: false, + containDetails: true, + ignoreDisabledUser: false, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + // Validate and Retrieve Dest User ... + var xDestUser = await ValidateUserExistsAndRetrieve( + destUserSelectByParam, + forceAdmin: false, + containDetails: true, + ignoreDisabledUser: false, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + var isSame = xSourceUser.Id == xDestUser.Id; + var isFollower = false; + var isFollowing = false; + var followerState = XFriendshipState.None; + var followingState = XFriendshipState.None; + + // + if (!isSame) + { + // + isFollower = await IsFollower( + userSelectByParam, + destUserSelectByParam, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + isFollowing = await IsFollowing( + userSelectByParam, + destUserSelectByParam, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + followerState = await GetFollowerState( + userSelectByParam, + destUserSelectByParam, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + followingState = await GetFollowingState( + userSelectByParam, + destUserSelectByParam, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + } + + // + var result = new XFriendshipInfoDto + { + UserId = xDestUser.Id, + IsFollower = isFollower, + IsFollowing = isFollowing, + FollowerState = followerState, + FollowingState = followingState, + Followers = xDestUser.Followers + .Count(f => f.State == XFriendshipState.Accepted), + Followings = xDestUser.Followings + .Count(f => f.State == XFriendshipState.Accepted) + }; + + // + return result; + } + #endregion + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Identity.cs + +using System; +using System.Net.Http; +using System.Threading.Tasks; +using IdentityModel.Client; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xExceptions.Constants; +using xExceptions.Models; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIds.Extensions; +using static IdentityModel.OidcConstants; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Identity Actions ... + /// + /// Request for Discovery Document + /// + /// an instance of DiscoveryDocumentResponse + public async Task RequestDiscoveryDocument() + { + // + var httpClient = GetHttpClient(); + var result = httpClient + .GetDiscoveryDocumentAsync(IdentityResourceConfiguration.Authority) + .ContinueWith(docTask => + { + // + httpClient.Dispose(); + return docTask.Result; + }); + + // + return await result; + } + + /// + /// Request AccessToken for Specific XApiScope + /// + /// a member of XApiScope + /// an instance of TokenResponse + public async Task RequestScopeAccessToken( + string scope + ) + { + // + // Validate Args ... + if (scope.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Disco Doc ... + var discoDoc = await RequestDiscoveryDocument(); + if (discoDoc.IsError) + { + XException.ActionFailed.Throw(); + } + + // + var request = new ClientCredentialsTokenRequest + { + Address = discoDoc.TokenEndpoint, + GrantType = GrantTypes.ClientCredentials, + ClientId = IdentityResourceConfiguration.ClientId, + ClientSecret = IdentityResourceConfiguration.ClientSecret, + Scope = scope + }; + + // + var httpClient = GetHttpClient(); + var result = await httpClient + .RequestClientCredentialsTokenAsync(request) + .ContinueWith(response => + { + // + httpClient.Dispose(); + return response.Result; + }); + + // + return result; + } + + /// + /// Authenticate a User + /// + /// an instance of XLoginRequest class which represent Authentication requirements + /// an instance of TokenResponse + public async Task Authenticate( + XLoginRequest model + ) + { + // + // Validate Args ... + ValidationProvider + .GroupValidationBuilder() + .AddNotNull(model) + .AddNotEmpty( + model.UserSelectBy, + model.Password + ) + .ValidateGroup(); + + // + // Retrieve Disco Doc ... + var discoDoc = await RequestDiscoveryDocument(); + if (discoDoc.IsError) + { + XException.ActionFailed.Throw(); + } + + // + // Create Request ... + var request = new PasswordTokenRequest + { + // + Address = discoDoc.TokenEndpoint, + + // + GrantType = GrantTypes.ClientCredentials, + ClientId = IdentityResourceConfiguration.ClientId, + ClientSecret = IdentityResourceConfiguration.ClientSecret, + + // + UserName = model.UserSelectBy, + Password = model.Password, + + // + // Pass Device to Request ... + Parameters = { { "force", false.ToJSON () } + } + }; + + // + var httpClient = GetHttpClient(); + var result = await httpClient + .RequestPasswordTokenAsync(request) + .ContinueWith(response => + { + // + httpClient.Dispose(); + return response.Result; + }); + + // + return result; + } + + /// + /// Do Login based on XLoginRequest + /// + /// an instance of XLoginRequest class which represent Authentication requirements + /// an instance of XLoginResponse + public async Task Login( + XLoginRequest model + ) + { + // + // Validate Args ... + ValidationProvider + .GroupValidationBuilder() + .AddNotNull(model, model.Device) + .AddNotEmpty( + model.UserSelectBy, + model.Password + ) + .ValidateGroup(); + + // + // Get Token Response ... + // + // Retrieve Disco Doc ... + var discoDoc = await RequestDiscoveryDocument(); + Logger.LogInformation($"discoDoc: {discoDoc.ToJSON()}"); + + if (discoDoc.IsError) + { + XException.ActionFailed.Throw(); + } + + // + // Create Request ... + var request = new PasswordTokenRequest + { + // + Address = discoDoc.TokenEndpoint, + + // + GrantType = GrantTypes.ClientCredentials, + ClientId = IdentityResourceConfiguration.ClientId, + ClientSecret = IdentityResourceConfiguration.ClientSecret, + + // + UserName = model.UserSelectBy, + Password = model.Password, + + // + // Pass Device to Request ... + Parameters = { { "force", false.ToJSON () }, + { "device", model.Device.ToJSON () }, + { "language", model.Language } + } + }; + + // + var httpClient = GetHttpClient(); + var authResponse = await httpClient + .RequestPasswordTokenAsync(request) + .ContinueWith(response => + { + // + httpClient.Dispose(); + return response.Result; + }); + + // + // Check If Response Has Error ... + Logger.LogInformation($"AuthResponse: {authResponse.ToJSON()}"); + + // + if (authResponse.IsError) + { + throw authResponse.GetException(); + } + + // + // Create XLoginResponse Model ... + var result = authResponse.CreateXLoginResponse(); + + // + // Get ans Set User Profile ... + result.Profile = await GetUserProfileAsync(model.UserSelectBy, model.UserSelectBy); + + // + return result; + } + + /// + /// Refresh Tokens + /// + /// Authentication Tokens, instance of XTokenResponse + /// an instance of XTokenResponse + public async Task RefreshTokens( + XTokenResponse model + ) + { + // + // Validate Args ... + ValidationProvider + .GroupValidationBuilder() + .AddNotNull(model) + .AddNotEmpty( + model.AccessToken, + model.RefreshToken + ) + .ValidateGroup(); + + // + var authResponse = await RequestDiscoveryDocument() + .ContinueWith((discoTask) => + { + // + var discoDoc = discoTask.Result; + if (discoDoc.IsError) + { + XException.ActionFailed.Throw(); + } + + // + using (var httpClient = GetHttpClient()) + { + return httpClient.RequestRefreshTokenAsync(new RefreshTokenRequest + { + // + Address = discoDoc.TokenEndpoint, + GrantType = GrantTypes.RefreshToken, + ClientId = IdentityResourceConfiguration.ClientId, + ClientSecret = IdentityResourceConfiguration.ClientSecret, + RefreshToken = model.RefreshToken + }).Result; + } + }); + + // + // Check If Response Has Error ... + if (authResponse.IsError) + { + // + XError error = authResponse.ErrorDescription.FromJSON(); + throw error.ToException(); + } + + // + // Create XLoginResponse Model ... + var result = authResponse.CreateXTokenResponse(); + + // + return result; + } + #endregion + + // + #region Requirements ... + /// + /// Get an Instance of Http Client + /// + /// an instance of HttpClient + public HttpClient GetHttpClient() + { + // + HttpClient httpClient = null; + httpClient = new HttpClient(); + var httpClientHandler = new HttpClientHandler() + { + ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => + { + // + Logger.LogInformation( + $"SSL Handler: {Environment.NewLine} sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}" + ); + + // + return true; + }, + ClientCertificateOptions = ClientCertificateOption.Manual, + }; + + // + httpClient = new HttpClient(httpClientHandler); + + // + return httpClient; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+MessageProvider.cs + +using System; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Navigations; +using xMessageService.Models; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Message Provider ... + /// + /// Generate and Prepare an XMessage Instance for + /// User Invite Action + /// + /// specify destination language + /// reciever email address + /// invitation token + /// return url for invitation user to redirect + /// an instance of XMessage + private XMessage GetUserInviteMessage( + string lang, + string emailAddress, + string inviteToken, + string returnUrl + ) + { + // + ValidationProvider + .NotEmpty(lang, emailAddress, inviteToken, returnUrl); + ValidationProvider + .EmailAddress(emailAddress); + ValidationProvider + .Url(returnUrl); + + // + HandleMessageProviderPreperation(lang); + + // + var msgStr = IdentityMessageProvider.InviteMsg + + Environment.NewLine + + returnUrl + + "?t=" + + inviteToken; + + // + var message = new XMessage + { + Message = msgStr, + }; + message.Recievers.Add(emailAddress); + + // + return message; + } + + /// + /// Generate and Prepare an XMessage Instance for + /// New Device LoggedIn Action + /// + /// specify destination language + /// reciever email address + /// an instance of XDevice which represent user new Device + /// an instance of XMessage + private XMessage GetUserNewDeviceLoggedInMessage( + string lang, + string emailAddress, + XDevice device + ) + { + // + ValidationProvider + .NotEmpty(lang, emailAddress); + ValidationProvider + .EmailAddress(emailAddress); + ValidationProvider + .NotNull(device); + + // + HandleMessageProviderPreperation(lang); + + // + // Generate Specific Message ... + var msgStr = IdentityMessageProvider.NewDeviceLoggedInMsg + + Environment.NewLine + + nameof(XDevice.Os) + ": " + device.Os + + Environment.NewLine + + nameof(XDevice.OsVersion) + ": " + device.OsVersion + + Environment.NewLine + + nameof(XDevice.Browser) + ": " + device.Browser + + Environment.NewLine + + nameof(XDevice.UserAgent) + ": " + device.UserAgent; + + // + // Create XMessage Instance ... + var message = new XMessage + { + Message = msgStr, + }; + message.Recievers.Add(emailAddress); + + // + return message; + } + + /// + /// Generate and Prepare an XMessage Instance for + /// Verification Code Action + /// + /// specify destination language + /// an string which points to a user email address or mobile number + /// user verification code + /// an instance of XMessage + private XMessage GetVerificationCodeMessage( + string lang, + string mobileNumberOrEmail, + string verificationCode + ) + { + // + ValidationProvider + .NotEmpty(lang, mobileNumberOrEmail, verificationCode); + + // + HandleMessageProviderPreperation(lang); + + // + mobileNumberOrEmail = mobileNumberOrEmail.ToNormalString(); + + // + // Validate Mobile or Email ... + var isValidEmail = mobileNumberOrEmail.IsValidEmail(); + var isValidMobileNumber = mobileNumberOrEmail.IsValidMobileNumber(); + if (!isValidEmail && + !isValidMobileNumber) + { + XException.InvalidData.Throw(); + } + + // + // Prepare Message String ... + var msgStr = IdentityMessageProvider.VerificationCodeMsg + + Environment.NewLine + + verificationCode; + + // + // Create XMessage Instance ... + var message = new XMessage + { + Message = msgStr, + }; + message.Recievers.Add(mobileNumberOrEmail); + + // + return message; + } + + /// + /// Generate and Prepare an XMessage Instance for + /// Registration Approve Action + /// + /// specify destination language + /// reciever email address + /// a token which approved user action + /// return url for redirect + /// an instance of XMessage + private XMessage GetRegistrationConfirmMessage( + string lang, + string emailAddress, + string actionToken, + string returnUrl + ) + { + // + ValidationProvider + .NotEmpty(lang, emailAddress, actionToken, returnUrl); + ValidationProvider + .EmailAddress(emailAddress); + ValidationProvider + .Url(returnUrl); + + // + HandleMessageProviderPreperation(lang); + + // + // Prepare Message Str ... + var msgStr = IdentityMessageProvider.RegistrationApproveMsg + + Environment.NewLine + + returnUrl + + "?t=" + + actionToken; + + // + var message = new XMessage + { + Message = msgStr, + }; + message.Recievers.Add(emailAddress); + + // + return message; + } + + /// + /// Generate and Prepare an XMessage Instance for + /// Registration Finished Action + /// + /// specify destination language + /// reciever email address + /// return url for redirect + /// an instance of XMessage + private XMessage GetRegistrationFinishedMessage( + string lang, + string emailAddress, + string returnUrl + ) + { + // + ValidationProvider + .NotEmpty(lang, emailAddress, returnUrl); + ValidationProvider + .EmailAddress(emailAddress); + ValidationProvider + .Url(returnUrl); + + // + HandleMessageProviderPreperation(lang); + + // + var msgStr = IdentityMessageProvider.RegisteredMsg + + Environment.NewLine + + returnUrl; + + // + var message = new XMessage + { + Message = msgStr, + }; + message.Recievers.Add(emailAddress); + + // + return message; + } + + /// + /// Generate and Prepare an XMessage Instance for + /// Password Changed Action + /// + /// specify destination language + /// reciever email address + /// return url for redirect + /// specify throw exceptions on failure or not, default is true + /// an instance of XMessage + private XMessage GetPasswordChangedMessage( + string lang, + string emailAddress, + string returnUrl, + bool throwException = true + ) + { + // + if (throwException) + { + ValidationProvider + .NotEmpty(lang, emailAddress, returnUrl); + ValidationProvider + .EmailAddress(emailAddress); + ValidationProvider + .Url(returnUrl); + } + + // + HandleMessageProviderPreperation(lang); + + // + var msgStr = IdentityMessageProvider.PasswordChangedMsg + + (returnUrl.IsNullOrEmpty() ? + "" : + Environment.NewLine + + returnUrl); + + // + var message = new XMessage + { + Message = msgStr, + }; + message.Recievers.Add(emailAddress); + + // + return message; + } + + /// + /// Generate and Prepare an XMessage Instance for + /// Reset Password Action + /// + /// specify destination language + /// reciever email address + /// a token which approved user action + /// return url for redirect + /// an instance of XMessage + private XMessage GetResetPasswordMessage( + string lang, + string emailAddress, + string actionToken, + string returnUrl + ) + { + // + ValidationProvider + .NotEmpty(lang, emailAddress, actionToken, returnUrl); + ValidationProvider + .EmailAddress(emailAddress); + ValidationProvider + .Url(returnUrl); + + // + HandleMessageProviderPreperation(lang); + + // + // Prepare Message Str ... + var msgStr = IdentityMessageProvider.ChangePasswordMsg + + Environment.NewLine + + returnUrl + + "?t=" + + actionToken; + + // + var message = new XMessage + { + Message = msgStr, + }; + message.Recievers.Add(emailAddress); + + // + return message; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Open.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Dtos; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region OpenGet ... + public async Task OpenGet( + XOpenActionInnerDto model + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(model) + .AddNotEmpty(model.Token) + .AddNotEmpty(model.Payload) + .ValidateGroupAsync(); + + // + return await OpenGet( + token: model.Token, + actionRequest: model.Payload + ); + } + + // + public async Task OpenGet( + string token, + string actionRequest + ) + { + // + // Validatr Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(token) + .AddNotEmpty(actionRequest) + .ValidateGroupAsync(); + // + // Try to Decrypt Request ... + var requestModel = ParseModel( + token: token, + request: actionRequest + ); + if (requestModel.IsNull() || + requestModel.Action.IsNullOrEmpty()) + { + XException.BadRequest.Throw(); + } + + // + var responseJson = string.Empty; + switch (requestModel.Action) + { + // + case XOpenActions.GetUserInfo: + // + ValidationProvider.NotEmpty(requestModel.Payload); + var userInfo = await GetUserInfo(requestModel); + if (userInfo.IsNull()) + { + XException.InvalidData.Throw(); + } + + // + responseJson = userInfo.ToJSON(); + break; + + // + case XOpenActions.GetUserInfos: + ValidationProvider.NotEmpty(requestModel.Payload); + var userInfos = await GetUserInfos(requestModel); + if (userInfos.IsNull()) + { + XException.InvalidData.Throw(); + } + + // + responseJson = userInfos.ToJSON(); + break; + } + + // + // Encrypt Json Str ... + var responseStr = SecurityProvider.Encrypt(responseJson); + requestModel.Payload = responseStr; + requestModel.Timestamp = DateTime.UtcNow + .AddMinutes(5) + .ToTimestamp() + .ToString(); + + // + token = requestModel.ToOpenActionToken(); + requestModel.Checksum = token; + var result = new XOpenActionInnerDto + { + Token = token, + Payload = requestModel.ToJSON() + }; + + // + // Check Result ... + if (result.IsNull()) + { + XException.ActionFailed.Throw(); + } + + // + return result; + } + #endregion + + public async Task GetUserInfo( + XOpenActionRequestDto model + ) + { + // + // Retrieve User Profile ... + var userProfile = await GetUserProfileAsync( + checkIsBanned: false, + checkCanLoginPolicies: false, + requestedUserSelectByParam: "", + forceCheckRequestedUser: false, + userSelectByParam: model.Payload + ); + + // + // Generate a Dynamic Object ... + var result = new XOpenActionUserInfoDto + { + Username = userProfile.UserName, + Firstname = userProfile.FirstName, + Lastname = userProfile.LastName, + Avatar = userProfile.Avatar + }; + + // + return result; + } + + public async Task> GetUserInfos( + XOpenActionRequestDto model + ) + { + // + var ids = model.Payload.ParseListString().ToList(); + var profiles = await GetUserProfilesAsync( + ids: ids, + checkIsBanned: false, + checkCanLoginPolicies: false, + requestedUserSelectByParam: "", + forceCheckRequestedUser: false + ); + var result = profiles.Select(x => new XOpenActionUserInfoDto + { + Avatar = x.Avatar, + Username = x.UserName, + Lastname = x.LastName, + Firstname = x.FirstName, + }); + + // + return result; + } + + // + #region Private ... + private XOpenActionRequestDto ParseModel( + string token, + string request + ) + { + // + // Decript request to get Json string of request object ... + var result = new XOpenActionRequestDto(); + + // + var jsonStr = SecurityProvider.Decrypt(request); + result = jsonStr.FromJSON(); + if (result.IsNull() || + !result.Validate() || + token != result.Checksum + ) + { + // + result = null; + return result; + } + + // + var modelChecksum = result.ToOpenActionToken(); + result = + token == modelChecksum + ? result + : null; + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Private.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using xCommons.Extensions; +using xCommons.Helpers; +using xExceptions.Constants; +using xIdentityModels; +using xIdentityModels.Constants; +using xIdentityModels.Dtos; +using xIdentityModels.Models; +using xIdentityModels.Navigations; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Generators ... + /// + /// Generate a Random Number ... + /// + /// a long value + private long GenerateRandom() + { + return new Random().Next(100000, 999999); + } + #endregion + + // + #region UserSelectBy Actions ... + /// + /// Retrieve UserSelectByType based on Given Info + /// + /// a user identifier + /// a boolean value which specify the identifier + /// must be specific and not empty, default is true + /// a boolean value which specify the type + /// must be specific, default is false + /// a member of XUserSelectBy + private XUserSelectBy GetUserSelectByType( + string userSelectByParam, + bool forceUserSelectByParamNotEmpty = true, + bool forceUserSelectByTypeMustSpecified = false + ) + { + // + if (forceUserSelectByParamNotEmpty) + { + ValidationProvider.NotEmpty(userSelectByParam); + } + + // + var result = XUserSelectBy.NotSpecified; + var normalizedString = userSelectByParam.ToNormalString(); + if (userSelectByParam.IsValidEmail()) + { + // + // Find User by Email ... + result = XUserSelectBy.Email; + } + else if (userSelectByParam.IsValidMobileNumber()) + { + // + // Find User by Mobile Number ... + result = XUserSelectBy.MobileNumber; + } + else if (userSelectByParam.IsGuid()) + { + // + // Find User By it's ID ... + result = XUserSelectBy.ID; + } + else + { + // + // UserName ... + result = XUserSelectBy.Username; + } + + // + if (forceUserSelectByTypeMustSpecified && + result == XUserSelectBy.NotSpecified) + { + XException.InvalidData.Throw(); + } + + // + return result; + } + + /// + /// Retrieve UserSelectByParam based on XUser Object + /// + /// an instance of XUser + /// a boolean value which specify the identifier must nut empty, + /// and throw exception if it is empty, default is true + /// a collection of XUserSelectBy members which + /// exclude them from result, default is null + /// a user identifier + private string GetUserSelectByParam( + XUser user, + bool forceNotNull = true, + ICollection excludes = null + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(user); + + // + var id = user.Id; + var userName = user.UserName; + var email = user.Email; + var phoneNumber = user.PhoneNumber; + + // + if (excludes != null) + { + if (excludes.Contains(XUserSelectBy.ID)) + { + id = null; + } + if (excludes.Contains(XUserSelectBy.Email)) + { + email = null; + } + if (excludes.Contains(XUserSelectBy.MobileNumber)) + { + phoneNumber = null; + } + if (excludes.Contains(XUserSelectBy.Username)) + { + userName = null; + } + } + + var selectByParam = + id.IsNullOrEmpty() ? + userName.IsNullOrEmpty() ? + email.IsNullOrEmpty() ? + phoneNumber.IsNullOrEmpty() ? null : phoneNumber : email : userName : id; + + // + // Check result ... + if (forceNotNull && + selectByParam.IsNullOrEmpty()) + { + XException.InvalidData.Throw(); + } + + // + return selectByParam; + } + + /// + /// Retrieve UserSelectByParam based on XActionRequestContext instance + /// + /// an instance of XActionRequestContext + /// a boolean value which specify the identifier must nut empty, + /// and throw exception if it is empty, default is true + /// a collection of XUserSelectBy members which + /// exclude them from result, default is null + /// a user identifier + private string GetUserSelectByParam( + XActionRequestContext context, + bool forceNotNull = true, + ICollection excludes = null + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(context); + + // + // Generate User SelectBy ... + var id = context.UserId; + var userName = context.UserName; + var email = context.Email; + var phoneNumber = context.MobileNumber; + + // + if (excludes != null) + { + // + if (excludes.Contains(XUserSelectBy.ID)) + { + id = null; + } + if (excludes.Contains(XUserSelectBy.Email)) + { + email = null; + } + if (excludes.Contains(XUserSelectBy.MobileNumber)) + { + phoneNumber = null; + } + if (excludes.Contains(XUserSelectBy.Username)) + { + userName = null; + } + } + + var selectByParam = + id.IsNullOrEmpty() ? + userName.IsNullOrEmpty() ? + email.IsNullOrEmpty() ? + phoneNumber.IsNullOrEmpty() ? null : phoneNumber : email : userName : id; + + // + // Check param ... + if (forceNotNull && + selectByParam.IsNullOrEmpty()) + { + XException.InvalidData.Throw(); + } + + // + return selectByParam; + } + + /// + /// Retrieve UserSelectByParam based on XActionRequestToken instance + /// + /// an instance of XActionRequestToken which + /// provides required informations + /// a boolean value which specify the identifier must nut empty, + /// and throw exception if it is empty, default is true + /// a collection of XUserSelectBy members which + /// exclude them from result, default is null + /// a user identifier + private string GetUserSelectByParam( + XActionRequestToken request, + bool forceNotNull = true, + ICollection excludes = null + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(request); + + // + // Generate User SelectBy ... + return GetUserSelectByParam( + context: request.Context, + forceNotNull: forceNotNull, + excludes: excludes + ); + } + + /// + /// Retrieve Available UserSelectByParams from given XUser + /// + /// an instance of XUser + /// + private ICollection GenerateUserSelectByParams( + XUser user + ) + { + // + if (user == null) + { + return null; + } + + // + var result = new List { + user.Id, + user.UserName, + user.PhoneNumber, + user.Email, + }; + + // + return result; + } + + /// + /// Retrieve Required XUserSelectByTypes + /// + /// a collection of XUserSelectBy members which + /// ignored in result + /// a collection of available XUserSelectBy members + private ICollection GenerateUserSelectByExcludes( + ICollection ignores + ) + { + // + var result = ObjectHelper + .ToEnumerableValues() + .Except(ignores); + + // + return result.ToList(); + } + #endregion + + // + #region XAction Actions ... + /// + /// Retrieve Action Result Response based on XToken + /// + /// an instance of XToken + /// an instance of XActionResponse + private XActionResponse GetActionResultResponse( + XToken xToken + ) + { + // + ValidationProvider.NotNull(xToken); + ValidationProvider.NotEmpty(xToken.Hash, xToken.Token); + + // + ValidateToken(xToken.Token); + + // + var expDate = GetTokenExpirationDate(xToken.Token); + ValidationProvider.NotNull(expDate); + + // + // Generate Result class ... + var result = new XActionResponse + { + Token = xToken.Hash, + Expiration = expDate + }; + + // + return result; + } + #endregion + + // + #region XIdentityMessage Actions ... + /// + /// Prepare Message Provider and Check it's State + /// + /// specify destination language + private void HandleMessageProviderPreperation( + string lang + ) + { + // + ValidationProvider + .NotEmpty(lang); + + // + // Prepare Identity Messages ... + // the PrepareMessageMethod check IsReady of Helper automatically ... + IdentityMessageProvider.PrepareMessages(lang); + + // + // Check Message Provider ... + var isMessageProviderReady = MessageProvider.IsReady(true); + if (!isMessageProviderReady) + { + XException.MessageServiceInitialFailed.Throw(); + } + } + #endregion + + // + #region Friendship Actions ... + /// + /// Check Follow Request Sent Before + /// from dest to source + /// + /// an instance of XUser + /// an instance of XUser + /// a boolean value + private bool IsFollowRequested( + XUser source, + XUser dest + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(source, dest); + + // + var result = source.Followings.Any(f => f.DestId == dest.Id); + + // + return result; + } + + /// + /// Check Follow Request Sent Before + /// from dest to source + /// + /// an instance of XUser + /// an instance of XUser + /// a member of XFriendshipState + /// a boolean value + private bool IsFollowRequested( + XUser source, + XUser dest, + XFriendshipState state + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(source, dest); + + // + var result = source.Followings + .Any(f => + f.DestId == dest.Id && + f.State == state); + + // + return result; + } + + /// + /// Converts to XFriendDto ... + /// + /// + /// + private async Task ToDto(XFriendshipFollower entity) + { + // + XFriendDto result = null; + + // + if (entity.IsNull()) + { + return result; + } + + // + // Reading Dest User Profile ... + var profile = await GetUserProfileAsync( + userSelectByParam: entity.DestId, + requestedUserSelectByParam: entity.UserId, + forceCheckRequestedUser: false, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + // Preparing Result ... + result = new XFriendDto + { + UserId = profile.UserId, + Avatar = profile.Avatar, + Username = profile.UserName, + Lastname = profile.LastName, + Firstname = profile.FirstName, + Type = XFriendshipType.Follower, + State = profile.FriendshipInfo.FollowerState, + }; + + // + return result; + } + + /// + /// Converts to XFriendDto ... + /// + /// + /// + private async Task ToDto(XFriendshipFollowing entity) + { + // + XFriendDto result = null; + + // + if (entity.IsNull()) + { + return result; + } + + // + // Reading Dest User Profile ... + var profile = await GetUserProfileAsync( + checkIsBanned: false, + checkCanLoginPolicies: false, + forceCheckRequestedUser: false, + userSelectByParam: entity.DestId, + requestedUserSelectByParam: entity.UserId + ); + + // + // Preparing Result ... + result = new XFriendDto + { + UserId = profile.UserId, + Avatar = profile.Avatar, + Username = profile.UserName, + Lastname = profile.LastName, + Firstname = profile.FirstName, + Type = XFriendshipType.Following, + State = profile.FriendshipInfo.FollowingState, + }; + + // + return result; + } + #endregion + + // + #region User Profile ... + /// + /// Count User's Page + /// + /// an integer value which represent page size + /// an inetger value which represent pages count + public async Task UserPagesCount( + int pageSize + ) + { + // + int count = await UserManager.Users.CountAsync(); + int pagesCount = count / pageSize; + + // + if (count % pageSize > 0) + { + pagesCount++; + } + + // + return pagesCount; + } + + /// + /// Count Avatar's Page + /// + /// an integer value which represent page size + /// an inetger value which represent pages count + public async Task ProfileImagePagesCount( + int pageSize + ) + { + // + int count = await DbContext.Avatars.CountAsync(); + int pagesCount = count / pageSize; + + // + if (count % pageSize > 0) + { + pagesCount++; + } + + // + return pagesCount; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Profile.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Dtos; +using xIdentityModels.Models; +using xIdentityModels.Navigations; +using xModels.Dtos; +using xIds.Extensions; +using xIdentityModels.Extensions; +using xIdentityHelper; +using xIdentityHelper.Extensions; +using xDataService.Extensions; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Retrieve Actions ... + /// + /// Retrieve User Profile + /// + /// a user identifier + /// requested user's identifier + /// specify check requested user's identifier not empty + /// and throw exception if it is, default true + /// check a user can log in in system or not, default is true + /// check user is banned or not, default is true + /// an instance of XUserProfileDto + public async Task GetUserProfileAsync( + string userSelectByParam, + string requestedUserSelectByParam, + bool forceCheckRequestedUser = true, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ) + { + // + // Data Validation ... + if (forceCheckRequestedUser) + { + ValidationProvider.NotEmpty( + userSelectByParam, + requestedUserSelectByParam); + } + else + { + ValidationProvider.NotEmpty(userSelectByParam); + } + + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + forceAdmin: false + ); + + // + var isAdmin = false; + var isSameUser = false; + if (forceCheckRequestedUser && + !requestedUserSelectByParam.IsNullOrEmpty()) + { + // + var xRequestedUser = await ValidateUserExistsAndRetrieve( + requestedUserSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + forceAdmin: false + ); + + // + isSameUser = xUser.Id == xRequestedUser.Id; + + // + var xRequestUserRoleNames = await GetRoleNamesAsync( + userSelectByParam: requestedUserSelectByParam, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + isAdmin = xRequestUserRoleNames.Contains(XUserRole.Admin.GetStringValue()); + } + + // + var roleNames = await GetRoleNamesAsync( + userSelectByParam, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + var xFriendshipInfo = + requestedUserSelectByParam.IsNullOrEmpty() + ? null + : await GetFriendshipInfo( + requestedUserSelectByParam, + userSelectByParam, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies + ); + + // + var result = xUser.ToXProfileDto( + isAdmin: isAdmin, + roles: roleNames, + isSameUser: isSameUser, + friendship: xFriendshipInfo, + profileConfig: Configuration.Policy.Profile + ); + + // + return result; + } + + /// + /// Retrieve a Collection of User Profiles + /// + /// a collection of user identifiers + /// requested user's identifier + /// specify check requested user's identifier not empty + /// and throw exception if it is, default true + /// check a user can log in in system or not, default is true + /// check user is banned or not, default is true + /// an collection of XUserProfileDto instances + public async Task> GetUserProfilesAsync( + ICollection ids, + string requestedUserSelectByParam, + bool forceCheckRequestedUser = true, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ) + { + // + // Data Validation ... + if (forceCheckRequestedUser) + { + ValidationProvider.NotEmpty(requestedUserSelectByParam); + } + ValidationProvider.NotZeroChilds(ids); + + // + var result = new List(); + foreach (var id in ids) + { + // + var xProfile = await GetUserProfileAsync( + id, + requestedUserSelectByParam, + checkIsBanned: checkIsBanned, + checkCanLoginPolicies: checkCanLoginPolicies, + forceCheckRequestedUser: forceCheckRequestedUser + ); + result.Add(xProfile); + } + + // + return result; + } + + /// + /// Query User Profiles + /// + /// requested user's identifier + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XUserProfileDto + public async Task> QueryUsers( + string requestedUserSelectByParam, + XQuery query + ) + { + // + // Validation ... + if (query.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + query = query.NormalizeQuery(DataConfiguration.ToXDataServiceConfig()); + + // + // Since in Resourceable Entities we have to Search on Locales + // we Must Implement Senario Custom ... + var totalEntities = GetUsersDbSet() + .ToList() + .Where(u => !u.ContainsUserSelectByParam(requestedUserSelectByParam)) + .Select(u => u.Id) + .ToList(); + + // + var items = await GetUserProfilesAsync( + totalEntities, + requestedUserSelectByParam, + checkIsBanned: false, + checkCanLoginPolicies: false, + forceCheckRequestedUser: false + ); + + // + 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 + ); + } + + // + // Generate Result Object ... + // Query = query, + var result = new XQueryResult + { + Items = items, + Page = query.Page, + PageSize = query.PageSize, + TotalPages = totalPagesCount, + TotalItems = totalItemsCount, + TotalFilteredPages = filteredPagesCount, + TotalFilteredItems = filteredItemsCount + }; + + // + return result; + } + + /// + /// Query In Roles User Profiles + /// + /// requested user's identifier + /// an string which represent user role + /// how to filter results based on XQuery structure + /// if it's true the user must has exact role, otherwise top level users also listed, default is false + /// an instance of XQueryResult of XUserProfileDto + public async Task> QueryInRoleUsers( + string requestedUserSelectByParam, + string role, + XQuery query, + bool forceRole = false + ) + { + // + // Validation ... + if (query.IsNull() || role.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Role Exists ... + var isExistsRole = await IsRoleExistsAsync(role); + if (!isExistsRole) + { + XException.NotFound.Throw(); + } + + // + // Normalize ... + query = query.NormalizeQuery(DataConfiguration.ToXDataServiceConfig()); + + // + #region Select Users Based on Specific Roles ... + var selectedUsers = new List(); + var usersEnumerator = GetUsersDbSet(query.ContainsDetail).AsAsyncEnumerable(); + await + foreach (var user in usersEnumerator) + { + // + if (user.ContainsUserSelectByParam(requestedUserSelectByParam)) + { + continue; + } + + // + // Set Default Value is True ... + var isInRole = false; + + // + // if Force Role ... + if (forceRole) + { + isInRole = await UserManager.IsInRoleAsync(user, role); + } + else + { + // + // Select User Top Role ... + var userRoles = await GetRoleNamesAsync( + checkIsBanned: false, + userSelectByParam: user.Id, + checkCanLoginPolicies: false + ); + isInRole = userRoles.HasRolePermissions(role); + } + + // + if (isInRole) + { + selectedUsers.Add(user.Id); + } + } + #endregion + + // + var items = await GetUserProfilesAsync( + selectedUsers, + requestedUserSelectByParam, + checkIsBanned: false, + checkCanLoginPolicies: false, + forceCheckRequestedUser: false + ); + + // + 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 + ); + } + + // + // Generate Result Object ... + // Query = query, + var result = new XQueryResult + { + Items = items, + Page = query.Page, + PageSize = query.PageSize, + TotalPages = totalPagesCount, + TotalItems = totalItemsCount, + TotalFilteredPages = filteredPagesCount, + TotalFilteredItems = filteredItemsCount + }; + + // + return result; + } + + /// + /// Query Specified User's Profile Images + /// + /// a user identifier + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XProfileImage + public async Task> QueryAvatars( + string userSelectByParam, + XQuery query + ) + { + // + // Validation ... + if (query.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + query = query.NormalizeQuery(DataConfiguration.ToXDataServiceConfig()); + + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: true, + checkIsBanned: true + ); + + // + var items = query.ContainsDetail ? + DbContext.Avatars + .Include(pf => pf.User) + .AsEnumerable() : + DbContext.Avatars + .AsEnumerable(); + items = items + .Where(pfi => pfi.UserId == xUser.Id); + + // + 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 + ); + } + + // + // Generate Result Object ... + // Query = query, + var result = new XQueryResult + { + Items = items, + Page = query.Page, + PageSize = query.PageSize, + TotalPages = totalPagesCount, + TotalItems = totalItemsCount, + TotalFilteredPages = filteredPagesCount, + TotalFilteredItems = filteredItemsCount + }; + + // + return result; + } + + /// + /// Check specific user Confirmed Email or not + /// + /// a user identifier + /// a boolean value + public async Task IsConfirmedEmail( + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + forceAdmin: false + ); + + // + return xUser.EmailConfirmed; + } + + /// + /// Ceck specific user Confirmed Mobile or not + /// + /// a user identifier + /// a boolean value + public async Task IsConfirmedMobile( + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + forceAdmin: false + ); + + // + return xUser.PhoneNumberConfirmed; + } + #endregion + + // + #region Update Actions ... + /// + /// Update User Profile (FirstName/LastName/DateOfBirth) ... + /// + /// a user identifier + /// requested user's identifier + /// user update info, an instance of XProfileUpdateRequest + /// an instance of XUserProfileDto + public async Task ProfileUpdateAsync( + string userSelectByParam, + string requestedUserSelectByParam, + XProfileUpdateRequest model + ) + { + // + // Data Validation ... + ValidationProvider.NotEmpty( + userSelectByParam, + requestedUserSelectByParam); + ValidationProvider.NotNull(model); + + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + forceAdmin: false + ); + + // + var xRequestedUser = await ValidateUserExistsAndRetrieve( + requestedUserSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + forceAdmin: false + ); + + // + await ValidateUserForDangerousAction(xUser, xRequestedUser); + + // + // FirstName ... + if (!model.FirstName.IsNullOrEmpty()) + { + xUser.FirstName = model.FirstName; + } + + // + // Last Name ... + if (!model.LastName.IsNullOrEmpty()) + { + xUser.LastName = model.LastName; + } + + // + // Date of Birth ... + if (model.DateOfBirth.HasValue) + { + xUser.DateOfBirth = model.DateOfBirth.Value; + } + + // + // Bio ... + xUser.Bio = model.Bio; + + // + // Cover Image ... + xUser.CoverImage = model.CoverImage; + + // + // Open To Search ... + xUser.OpenToSearch = model.OpenToSearch; + + // + var updateResult = await UpdateUserAsync(xUser); + if (!updateResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + var result = await GetUserProfileAsync(userSelectByParam, requestedUserSelectByParam); + return result; + } + + /// + /// Update User Profile + /// + /// a user identifier + /// requested user's identifier + /// user update info, an instance of XProfileUpdateRequest + /// an instance of XUserProfileDto + public async Task FullProfileUpdateAsync( + string userSelectByParam, + string requestedUserSelectByParam, + XProfileUpdateRequest model + ) + { + // + // Data Validation ... + ValidationProvider.NotEmpty( + userSelectByParam, + requestedUserSelectByParam); + ValidationProvider.NotNull(model); + + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + forceAdmin: false + ); + + // + var xRequestedUser = await ValidateUserExistsAndRetrieve( + requestedUserSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + forceAdmin: false + ); + + // + await ValidateUserForDangerousAction(xUser, xRequestedUser); + + // + #region Fill User with Models Value ... + // + // FirstName ... + xUser.FirstName = !model.FirstName.IsNullOrEmpty() ? + model.FirstName : + xUser.FirstName; + // + // LastName ... + xUser.LastName = !model.LastName.IsNullOrEmpty() ? + model.LastName : + xUser.LastName; + + // + // Email ... + xUser.Email = !model.Email.IsNullOrEmpty() ? + model.Email : + xUser.Email; + + // + // EmailConfirmed ... + if (model.EmailConfirmed.HasValue) + { + xUser.EmailConfirmed = model.EmailConfirmed.Value; + } + + // + // PhoneNumber ... + xUser.PhoneNumber = !model.PhoneNumber.IsNullOrEmpty() ? + model.PhoneNumber : + xUser.PhoneNumber; + + // + // PhoneNumberConfirmed ,,, + if (model.PhoneNumberConfirmed.HasValue) + { + xUser.PhoneNumberConfirmed = model.PhoneNumberConfirmed.Value; + } + + // + // DateOfBirth ... + if (model.DateOfBirth.HasValue) + { + xUser.DateOfBirth = model.DateOfBirth.Value; + } + + // + // CreationDate ... + if (model.CreationDate.HasValue) + { + xUser.CreationDate = model.CreationDate.Value; + } + + // + // LastLogin ... + if (model.LastLogin.HasValue) + { + xUser.LastLogin = model.LastLogin.Value; + } + + // + // Avatar ... + xUser.Avatar = !model.Avatar.IsNullOrEmpty() ? + model.Avatar : + xUser.Avatar; + + // + // Gender ... + if (model.Gender.HasValue) + { + xUser.Gender = model.Gender.Value; + } + + // + // IsEnable ... + if (model.IsEnable.HasValue) + { + xUser.IsEnable = model.IsEnable.Value; + } + + // + // IsBanned ... + if (model.IsBanned.HasValue) + { + xUser.IsBanned = model.IsBanned.Value; + } + + // + // Bio ... + xUser.Bio = model.Bio; + + // + // Cover Image ... + xUser.CoverImage = model.CoverImage; + + // + // Open To Search ... + xUser.OpenToSearch = model.OpenToSearch; + #endregion + + // + var updateResult = await UpdateUserAsync( + xUser, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + if (!updateResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + // Handle Roles ... + + // + // Remove User from All Roles ... + if (xUser.Roles.HasChild()) + { + // + var roleNames = await GetRoleNamesAsync( + xUser.Id, + checkIsBanned: false, + checkCanLoginPolicies: false + ); + + // + foreach (var role in roleNames) + { + // + var isRoleExists = await IsRoleExistsAsync(role); + if (isRoleExists) + { + // + var isInRole = await UserManager.IsInRoleAsync(xUser, role); + if (!isInRole) + { + // + var removeRoleResult = await UserManager.RemoveFromRoleAsync(xUser, role); + if (!removeRoleResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + } + } + } + } + + // + // Check Roles and Add to Exists ... + if (model.Roles.HasChild()) + { + // + // Check Exists Roles for Add ... + foreach (var role in model.Roles) + { + // + var isRoleExists = await IsRoleExistsAsync(role); + if (isRoleExists) + { + // + var isInRole = await UserManager.IsInRoleAsync(xUser, role); + if (!isInRole) + { + // + var addToRoleResult = await UserManager.AddToRoleAsync(xUser, role); + if (!addToRoleResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + } + } + } + } + + // + var result = await GetUserProfileAsync( + userSelectByParam, + requestedUserSelectByParam, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + return result; + } + #endregion + + // + #region Request For Actions ... + /// + /// Request Mobile Confirm + /// + /// specify destination language + /// an instance of XDevice + /// a user identifier + /// user's password + /// mobile number which need to confirm + /// an instance of XActionResponse + public async Task RequestConfirmMobile( + string lang, + XDevice device, + string userSelectByParam, + string password, + string mobileNumber = null + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + lang, + userSelectByParam, + password) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + await ValidateDeviceForActions(device); + + // + // Prepare Request Context ... + var context = new XActionRequestContext + { + Lang = lang, + Device = device, + MobileNumber = mobileNumber + }; + + // + // Validate and Retrieve User ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + checkIsBanned: false); + + // + context.UserName = user.UserName; + + // + await ValidateUserAndPassword( + userSelectByParam, + password, + false, + XException.ActionFailed.ToException()); + + // + ValidationProvider.MobileNumber(user.PhoneNumber); + if (!mobileNumber.IsNullOrEmpty() && + user.PhoneNumber == mobileNumber && + user.PhoneNumberConfirmed) + { + XException.MobileInUsed.Throw(); + } + + // + if (mobileNumber.IsNullOrEmpty()) + { + mobileNumber = user.PhoneNumber; + } + + // + // Remove All Prevoius Requests ... + await HandleRemoveExistsTokens(user.Id); + + // + // Prepare New Token Result , + // by Requesting an Action ... + var request = await ActionRequest( + lang, + device, + userSelectByParam, + XAction.RequestMobileVerificationCode, + context, + forceRenewToken: true + ); + + // + Logger.LogInformation($"ActionRequest: {request.ToJSON()}"); + + // + // Call Confirm Method of Registration ... + var result = await RequestMobileVerificationCode( + lang, + device, + request.Token, + mobileNumber, + checkMobileInUse: false); + + // + return result; + } + + /// + /// Request Email Confirma + /// + /// specify destination language + /// an instance of XDevice + /// a user identifier + /// user's password + /// email address which need to confirm + /// an instance of XActionResponse + public async Task RequestConfirmEmail( + string lang, + XDevice device, + string userSelectByParam, + string password, + string emailAddress = null + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + lang, + userSelectByParam, + password) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + await ValidateDeviceForActions(device); + + // + // Prepare Request Context ... + var context = new XActionRequestContext + { + Lang = lang, + Device = device, + Email = emailAddress + }; + + // + // Validate and Retrieve User ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + checkIsBanned: false); + + // + context.UserName = user.UserName; + + // + // Remove All Prevoius Requests ... + await HandleRemoveExistsTokens(user.Id); + + // + await ValidateUserAndPassword( + userSelectByParam, + password, + false, + XException.ActionFailed.ToException()); + + // + ValidationProvider.EmailAddress(user.Email); + if (!emailAddress.IsNullOrEmpty() && + user.Email == emailAddress && + user.EmailConfirmed) + { + XException.EmailInUsed.Throw(); + } + + // + if (emailAddress.IsNullOrEmpty()) + { + emailAddress = user.Email; + context.Email = user.Email; + } + + // + // Prepare New Token Result , + // by Requesting an Action ... + var request = await ActionRequest( + lang, + device, + userSelectByParam, + XAction.RequestEmailVerificationCode, + context, + forceRenewToken: true + ); + + // + // Call Confirm Method of Registration ... + var result = await RequestEmailVerificationCode( + lang, + device, + request.Token, + emailAddress, + checkEmailInUse: false); + + // + return result; + } + + /// + /// Request Reset Password + /// + /// specify destination language + /// an instance of XDevice + /// a user identifier + /// redirection url + /// an instance of XActionResponse + public async Task RequestResetPassword( + string lang, + XDevice device, + string userSelectByParam, + string returnUrl + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + lang, + userSelectByParam, + returnUrl) + .AddNotNull(device) + .AddUrl(returnUrl) + .ValidateGroupAsync(); + + // + await ValidateDeviceForActions(device); + + // + // Validate and Retrieve User ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: true, + checkIsBanned: true); + + // + // Prepare Request Context ... + var context = new XActionRequestContext + { + Lang = lang, + Device = device, + Email = user.Email, + UserName = user.UserName + }; + + // + // Remove All Prevoius Requests ... + await HandleCleanUserTokens(user); + + // + // Prepare New Token Result , + // by Requesting an Action ... + var result = await ActionRequest( + lang, + device, + user.Email, + XAction.RequestResetPassword, + context + ); + + // + // Prepare Message ... + var xMessage = GetResetPasswordMessage( + lang, + user.Email, + result.Token, + returnUrl); + + // + try + { + await MessageProvider.SendMailAsync(xMessage); + } + catch { } + + // + return result; + } + + /// + /// Request Registration Confrirm + /// + /// specify destination language + /// an instance of XDevice + /// a user identifier + /// user's password + /// redirection url + /// + public async Task RequestConfirmRegistration( + string lang, + XDevice device, + string userSelectByParam, + string password, + string returnUrl + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + lang, + userSelectByParam, + password, + returnUrl) + .AddNotNull(device) + .AddUrl(returnUrl) + .ValidateGroupAsync(); + + // + // Validate user and Retrieve it ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + checkIsBanned: true, + ignoreDisabledUser: true); + + // + // Prepare Request Context ... + var context = new XActionRequestContext + { + Lang = lang, + Device = device, + Email = user.Email + }; + + // + await ValidateUserAndPassword( + userSelectByParam, + password, + false, + XException.ActionFailed.ToException()); + + // + // Remove All Prevoius Requests ... + await HandleRemoveExistsTokens(user.Id); + + // + // Prepare New Token Result , + // by Requesting an Action ... + var result = await ActionRequest( + lang, + device, + user.Email, + XAction.Finish, + context + ); + + // + // Prepare XMessage Instance ... + var xMessage = GetRegistrationConfirmMessage( + lang, + user.Email, + result.Token, + returnUrl); + + // + // Send Message ... + try + { + await MessageProvider.SendMailAsync(xMessage); + } + catch { } + } + #endregion + + // + #region Password Actions ... + /// + /// Change Password + /// + /// specify destination language + /// an instance of XDevice + /// a user identifier + /// user's password + /// user's new password + /// redirection url + /// + public async Task ChangePassword( + string lang, + XDevice device, + string userSelectByParam, + string password, + string newPassword, + string returnUrl + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + lang, + userSelectByParam, + password, + newPassword, + returnUrl) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + // Password Unique ... + if (password == newPassword) + { + XException.PasswordsSame.Throw(); + } + + // + // Validate and Retrieve User ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: true, + checkIsBanned: true); + + // + await ValidateUserAndPassword( + userSelectByParam, + password); + + // + // Check Password Policies ... + await ValidatePasswordPolicies(user, newPassword); + + // + // Clean All User Tokens ... + await HandleCleanUserTokens(user); + + // + // Try to Change User Password ... + var passwordResetResult = await UserManager + .ChangePasswordAsync( + user, + password, + newPassword); + + // + // Check Result ... + if (!passwordResetResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + // Prepare Message ... + var xMessage = GetPasswordChangedMessage( + lang, + user.Email, + returnUrl, + throwException: false + ); + + // + // Send Message ... + try + { + await MessageProvider.SendMailAsync(xMessage); + } + catch { } + } + + /// + /// Reset User Password + /// + /// specify destination language + /// an instance of XDevice + /// a token which approved user action + /// user's new password + /// redirection url + /// + public async Task ResetPassword( + string lang, + XDevice device, + string actionToken, + string newPassword, + string returnUrl + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + lang, + actionToken, + newPassword, + returnUrl) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + // Get Related Token to Registration Hash and Validate it, + // then Parse XActionRequest Instance from Token ... + var request = await ValidateAndParseActionHash(actionToken); + + // + // Get User Identifier ... + var userSelectByParam = GetUserSelectByParam(request); + + // + // Validate and Retrieve User ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: true, + checkIsBanned: true); + + // + // Check Password Policies ... + await ValidatePasswordPolicies(user, newPassword); + + // + // Clean All User Tokens ... + await HandleCleanUserTokens(user); + + // + // Try to Remove User Password ... + var removePasswordResult = await UserManager.RemovePasswordAsync(user); + if (!removePasswordResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + // try to Add New Password ... + var passwordResetResult = await UserManager.AddPasswordAsync(user, newPassword); + if (!passwordResetResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + // Prepare Message ... + var xMessage = GetPasswordChangedMessage( + lang, + user.Email, + returnUrl); + + // + // Send Message ... + try + { + await MessageProvider.SendMailAsync(xMessage); + } + catch { } + } + #endregion + + // + #region Profile Image Actions ... + /// + /// Add Profile Image to a User + /// + /// a user identifier + /// an specific File to upload, IFormFile + /// an instance of XUserProfileDto + public async Task AddAvatar( + string userSelectByParam, + IFormFile file + ) + { + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + exception: XException.NotFound.ToException() + ); + + // + // Handle Saving File and Attach it to User ... + var saveResult = await StorageProvider.HandleProfileImageSave(file); + + // + var xProfileImage = new XProfileImage + { + UserId = xUser.Id, + Name = saveResult.FileName, + Path = saveResult.FilePath, + Thumb = saveResult.Thmbnail, + ThumbPath = saveResult.ThmbnailPath, + CreationDate = DateTime.UtcNow + }; + + // + xUser.Avatars.Add(xProfileImage); + var updateResult = await UpdateUserAsync( + xUser, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + if (!updateResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + xUser.Avatar = xProfileImage.ThumbPath; + updateResult = await UpdateUserAsync( + xUser, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + if (!updateResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + var result = await GetUserProfileAsync(userSelectByParam, userSelectByParam); + return result; + } + + /// + /// Add a Collection of Profile Images to a User + /// + /// a user identifier + /// a collection of Files to upload, IFormFileCollection + /// an instance of XUserProfileDto + public async Task AddAvatars( + string userSelectByParam, + IFormFileCollection files + ) + { + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + exception: XException.NotFound.ToException() + ); + + // + foreach (var file in files) + { + // + // Handle Saving File and Attach it to User ... + var saveResult = await StorageProvider.HandleProfileImageSave(file); + + // + var xProfileImage = new XProfileImage + { + UserId = xUser.Id, + Name = saveResult.FileName, + Path = saveResult.FilePath, + Thumb = saveResult.Thmbnail, + ThumbPath = saveResult.ThmbnailPath, + CreationDate = DateTime.UtcNow + }; + + // + xUser.Avatars.Add(xProfileImage); + } + + // + var updateResult = await UpdateUserAsync( + xUser, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + if (!updateResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + var result = await GetUserProfileAsync(userSelectByParam, userSelectByParam); + return result; + } + + /// + /// Set Specific Profie Image as Current Profile Image + /// + /// a user identifier + /// an integer which reperesent AvatarId to set as current Avatar + /// an instance of XUserProfileDto + public async Task SetAvatar( + string userSelectByParam, + int id + ) + { + // + ValidationProvider.NotEmpty(userSelectByParam); + + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + exception: XException.NotFound.ToException() + ); + + // + var xProfileImage = xUser.Avatars.FirstOrDefault(pf => pf.Id == id); + if (xProfileImage.IsNull()) + { + XException.NotFound.Throw(); + } + + // + xUser.Avatar = xProfileImage.ThumbPath; + var updateResult = await UpdateUserAsync( + xUser, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + if (!updateResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + var result = await GetUserProfileAsync(userSelectByParam, userSelectByParam); + return result; + } + + /// + /// Remove a Collection of User's Profile Images + /// + /// a user identifier + /// a comma seperated list of avatarIds to remove + /// an instance of XUserProfileDto + public async Task RemoveAvatar( + string userSelectByParam, + ICollection ids + ) + { + // + ValidationProvider.NotEmpty(userSelectByParam); + + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + exception: XException.NotFound.ToException() + ); + + // + var xProfileImages = xUser.Avatars + .Where(pf => ids.Contains(pf.Id)); + if (xProfileImages.HasChild()) + { + // + xUser.Avatars = xUser.Avatars + .Except(xProfileImages) + .ToList(); + + // + // Creat a List of Physical Files to Remove ... + var xFilePathsForRemove = xProfileImages.Select(pf => pf.Thumb).ToList(); + xFilePathsForRemove.AddRange(xProfileImages.Select(pf => pf.Name)); + + // + StorageProvider.DeleteFiles( + xFilePathsForRemove, + forceFileExists: false); + + // + var isCurrentProfileImage = xProfileImages.Any(pfi => pfi.ThumbPath == xUser.Avatar); + if (isCurrentProfileImage) + { + xUser.Avatar = ""; + } + + // + await UpdateUserAsync(xUser); + } + + // + var result = await GetUserProfileAsync(userSelectByParam, userSelectByParam); + return result; + } + #endregion + + // + #region Confirmations ... + /// + /// Confirm Registration + /// + /// specify destination language + /// an instance of XDevice + /// a token which approved user action + /// redirection url + /// + public async Task ConfirmRegistration( + string lang, + XDevice device, + string actionToken, + string returnUrl + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + lang, + actionToken, + returnUrl) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + // Check Device Validation ... + await ValidateDeviceForActions(device); + + // + // Get Related Token to Registration Hash and Validate it, + // then Parse XActionRequest Instance from Token ... + var request = await ValidateAndParseActionHash(actionToken); + if (request.Action != XAction.Finish) + { + XException.ActionFailed.Throw(); + } + + // + // Extract UserSelectByParam from XActionRequest ... + var userSelectByParam = GetUserSelectByParam( + request, + forceNotNull: true); + + // + // Validate user and Retrieve it ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + ignoreDisabledUser: true, + checkIsBanned: true); + + // + // Make User Enabled ... + var xMessage = GetRegistrationFinishedMessage( + lang, + user.Email, + returnUrl); + + // + user.IsEnable = true; + user.EmailConfirmed = true; + + // + await UpdateUserAsync( + user, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + // Remove Token ... + await RemoveTokenByHash(actionToken); + + // + // Remove All Prevoius Requests ... + await HandleRemoveExistsTokens(user.Id); + + // + await RemoveVerificationCodeRequestByDevice(device); + + // + // Send Mail ... + try + { + await MessageProvider.SendMailAsync(xMessage); + } + catch { } + } + + /// + /// Confirm Mobile Number + /// + /// specify destination language + /// an instance of XDevice + /// a token which approved user action + /// recieved verification code + /// an instance of XActionResponse + public async Task ConfirmMobileNumber( + string lang, + XDevice device, + string actionToken, + string verificationCode + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(lang, actionToken, verificationCode) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + // Check Device Validation ... + await ValidateDeviceForActions(device); + + // + // Get Related Token to Registration Hash and Validate it, + // then Parse XActionRequest Instance from Token ... + var request = await ValidateAndParseActionHash(actionToken); + ValidationProvider.MobileNumber(request.Context.MobileNumber); + + // + // Validate two Device Must Same ... + ValidateRequestAndGiveDevices(device, request.Context.Device); + + // + // Check XActionRequest Action Must be Request Mobile Validation ... + if (request.Action != XAction.RequestMobileVerificationCode) + { + XException.InvalidData.Throw(); + } + + // + // Extract UserSelectByParam from XActionRequest ... + var userSelectByParam = GetUserSelectByParam(request); + + // + // since device must Requested before, + // Retrieve XVerification instance by Verification Code from Db + // and Validate it ... + XVerificationRequest xVerificationRequest = await ValidateAndRetrieveVerificationRequest(verificationCode); + + // + // Check User Added To Db or Not ... + // var isUserExists = await IsUserExistsAsync(userSelectByParam); + var isAddedUser = !request.Context.UserId.IsNullOrEmpty(); + if (isAddedUser) + { + // + // Validate User Exists and Retireve User Object ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + checkIsBanned: false); + + // + // Update Exists User's Mobile Confirmation ... + user.PhoneNumberConfirmed = true; + + // + // Save Changes to Db ... + await UpdateUserAsync(user, checkCanLoginPolicies: false, checkIsBanned: false); + + // + request.Context.UserId = user.Id; + } + + // + // Update Request Context MobileNumber Verification Code + // and it's Confirmation Status ... + request.Context.MobileVerificationCode = verificationCode; + request.Context.MobileVerified = true; + + // + // Prepare New Token Result , + // by Requesting an Action ... + var result = await ActionRequest( + lang, + device, + userSelectByParam, + XAction.ConfirmMobileNumber, + request.Context, + forceCheckUserExists: isAddedUser + ); + + // + // Remove Previous Token ... + await RemoveTokenByHash(actionToken); + + try + { + // + // Remove XVerificationRequest instance from Db ... + await RemoveVerificationRequest(verificationCode); + } + catch { } + + // + // Return Result ... + return result; + } + + /// + /// Confirm Email Address + /// + /// specify destination language + /// an instance of XDevice + /// a token which approved user action + /// recieved verification code + /// an instance of XActionResponse + public async Task ConfirmEmailAddress( + string lang, + XDevice device, + string actionToken, + string verificationCode + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(lang, actionToken, verificationCode) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + // Check Device Validation ... + await ValidateDeviceForActions(device); + + // + // Get Related Token to Registration Hash and Validate it, + // then Parse XActionRequest Instance from Token ... + var request = await ValidateAndParseActionHash(actionToken); + ValidationProvider.EmailAddress(request.Context.Email); + + // + // Validate two Device Must Same ... + ValidateRequestAndGiveDevices(device, request.Context.Device); + + // + // Check XActionRequest Action Must be Request Mobile Validation ... + if (request.Action != XAction.RequestEmailVerificationCode) + { + XException.InvalidData.Throw(); + } + + // + // Extract UserSelectByParam from XActionRequest ... + var userSelectByParam = GetUserSelectByParam(request); + + // + // since device must Requested before, + // Retrieve XVerification instance by Verification Code from Db + // and Validate it ... + XVerificationRequest xVerificationRequest = await ValidateAndRetrieveVerificationRequest(verificationCode); + + // + // Check User Added To Db or Not ... + var isAddedUser = !request.Context.UserId.IsNullOrEmpty(); + if (isAddedUser) + { + // + // Validate User Exists and Retireve User Object ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + checkIsBanned: false); + + // + // Update Exists User's Email Confirmation ... + user.EmailConfirmed = true; + + // + // Save Changes to Db ... + await UpdateUserAsync(user, checkCanLoginPolicies: false, checkIsBanned: false); + + // + request.Context.UserId = user.Id; + } + + // + // Update Request Context Email Address Verification Code + // and it's Confirmation Status ... + request.Context.EmailVerificationCode = verificationCode; + request.Context.EmailVerified = true; + + // + // Prepare New Token Result , + // by Requesting an Action ... + var result = await ActionRequest( + lang, + device, + userSelectByParam, + XAction.ConfirmEmailAddress, + request.Context, + forceCheckUserExists: isAddedUser + ); + + // + // Remove Previous Token ... + await RemoveTokenByHash(actionToken); + + // + // Remove XVerificationRequest instance from Db ... + await RemoveVerificationRequest(verificationCode); + + // + // Return Result ... + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Query.cs + +using System.Linq; +using System.Threading.Tasks; +using xCommons.Extensions; +using xDataService.Extensions; +using xExceptions.Constants; +using xIdentityModels.Extensions; +using xIds.Extensions; +using xModels.Dtos; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + /// + /// Retrieve Users as Query Model for Query Service ... + /// + /// + /// + /// + /// + /// + public async Task> QueryUsers( + string requestedUserSelectByParam, + XQuery query, + string role = null, + bool forceRole = false + ) + { + // + // Validation ... + if (query.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + query = query.NormalizeQuery(DataConfiguration.ToXDataServiceConfig()); + + // + // Since in Resourceable Entities we have to Search on Locales + // we Must Implement Senario Custom ... + var items = GetUsersDbSet() + .ToList() + .Where(u => !u.ContainsUserSelectByParam(requestedUserSelectByParam)) + .ToList() + .AsEnumerable(); + + // + 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 + ); + } + + // + // Generate Result Object ... + // Query = query, + var result = new XQueryResult + { + Items = items + .Select(u => u.Id), + Page = query.Page, + PageSize = query.PageSize, + TotalPages = totalPagesCount, + TotalItems = totalItemsCount, + TotalFilteredPages = filteredPagesCount, + TotalFilteredItems = filteredItemsCount + }; + + // + return await Task.Run(() => result); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Registration.cs + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels; +using xIdentityModels.Constants; +using xIdentityModels.Models; +using xIdentityModels.Navigations; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Registration Actions ... + /// + /// Invite a User to Register on Dashboard + /// + /// specify destination language + /// an instance of XDevice + /// which email address is going to invite + /// return url for invitation user to redirect + /// an instance of XActionResponse + public async Task InviteUser( + string lang, + XDevice device, + string email, + string returnUrl + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + lang, + email, + returnUrl + ) + .AddNotNull(device) + .AddEmailAddress(email) + .ValidateGroupAsync(); + + // + // Check Email is Unique ... + await ValidateUserNotExists(email); + + // + // Get Request Result ... + var result = await ActionRequest( + lang, + device, + email, + XAction.Invite, + forceCheckContext: false, + forceCheckUserExists: false); + + // + // Prepare Propper Message ... + var xMessage = GetUserInviteMessage(lang, email, result.Token, returnUrl); + + // + // Send Message ... + try + { + await MessageProvider.SendMailAsync(xMessage); + } + catch { } + + // + // Send Result ... + return result; + } + + /// + /// Recieve some Basic Informations and Start Registration Proccess + /// if they Valid + /// + /// Registration Proccess Starts with Invoking this Action + /// + /// specify destination language + /// an instance of XDevice + /// optional, if user invited, this is the invitation token + /// user's FirstName + /// user's LastName + /// user's dob date + /// user's Mobile Number + /// user's Email address + /// an instance of XActionResponse + public async Task RequestRegistration( + string lang, + XDevice device, + string invitationHash, + string firstName, + string lastName, + DateTime dateOfBirth, + string mobileNumber = null, + string emailAddress = null + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + lang, + firstName, + lastName + ) + .AddNotNull( + device, + dateOfBirth + ) + .ValidateGroupAsync(); + + // + // Validate Mobile Number if Exists ... + if (!mobileNumber.IsNullOrEmpty()) + { + // + ValidationProvider.MobileNumber(mobileNumber); + + // + // Check Mobile is Uniqsue ... + await ValidateUserNotExists(mobileNumber); + } + + // + // Validate Email Address if Exists ... + if (!emailAddress.IsNullOrEmpty()) + { + // + ValidationProvider.EmailAddress(emailAddress); + + // + // Check Email is Unique ... + await ValidateUserNotExists(emailAddress); + } + + // + // Get Related Token and Validate it and + // Check Related Configurations ... + await ValidateInvitationHash(invitationHash); + + // + // Check Device Validation ... + await ValidateDeviceForActions(device); + + // + // Validate DateOfBirth ... + ValidateDateOfBirth(dateOfBirth); + + // + // Define Empty Objects for Using ... + var userSelectByParam = ""; + var context = new XActionRequestContext(); + // + context.Lang = lang; + context.Device = device; + context.LastName = lastName; + context.FirstName = firstName; + context.DateOfBirth = dateOfBirth; + context.MobileNumber = mobileNumber ?? ""; + context.Email = !emailAddress.IsNullOrEmpty() ? emailAddress.ToNormalString() : ""; + + // + userSelectByParam = GetUserSelectByParam(context); + + // + // Token Data Parsing ... + if (!invitationHash.IsNullOrEmpty()) + { + // + // Get Related Token, Validate it and Parse Contains + // XAction Request and Validate the Request itself and + // Return it ... + var inviteRequest = await ValidateAndParseActionHash(invitationHash); + + // + // Extract User Select By Param and Type ... + userSelectByParam = GetUserSelectByParam(inviteRequest); + var userSelectBy = GetUserSelectByType(userSelectByParam); + + // + // Check Invitation Only done with Email Address ... + if (userSelectBy != XUserSelectBy.Email) + { + XException.InvalidData.Throw(); + } + + // + // Check Invited Email and Given Email to be Same ... + if (emailAddress.IsNullOrEmpty() || + (!emailAddress.IsNullOrEmpty() && + !emailAddress.IsValidEmail())) + { + XException.InvalidEmailAddress.Throw(); + } + var isSameEmail = inviteRequest.Context.Email.ToNormalString() == emailAddress.ToNormalString(); + if (!isSameEmail) + { + XException.EmailsSame.Throw(); + } + + // + // Update Context Email for Request Action ... + context.Email = userSelectByParam.ToNormalString(); + context.EmailVerified = true; + } + + // + var result = await ActionRequest( + lang, + device, + userSelectByParam, + XAction.Registration, + context, + forceCheckUserExists: false + ); + + // + // Remove Invitation Token ... + if (!invitationHash.IsNullOrEmpty()) + { + await RemoveTokenByHash(invitationHash); + } + + // + // Return Result ... + return result; + } + + /// + /// Add User Account Info + /// + /// specify destination language + /// an instance of XDevice + /// a token which approved user action + /// user name + /// assigne password + /// an instance of XActionResponse + public async Task AddAcountInfo( + string lang, + XDevice device, + string actionToken, + string userName, + string password + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty( + lang, + actionToken, + userName, + password) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + // Check Device Validation ... + await ValidateDeviceForActions(device); + + // + // Validate a UserSelector is Available or not ... + await ValidateCanRegister(userName); + + // + // Check UserName is Unique ... + await ValidateUserNotExists(userName); + + // + // Get Related Token to Registration Hash and Validate it, + // then Parse XActionRequest Instance from Token ... + var request = await ValidateAndParseActionHash(actionToken); + + // + // Validate Requeired Data for Register User must be in Action Context ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull( + request.Context, + request.Context.Device, + request.Context.DateOfBirth) + .AddNotEmpty( + request.Context.Email, + request.Context.MobileNumber, + request.Context.FirstName, + request.Context.LastName) + .AddEmailAddress(request.Context.Email) + .AddMobileNumber(request.Context.MobileNumber) + .ValidateGroupAsync(); + + // + // Validate two Device Must Same ... + ValidateRequestAndGivenDevices(device, request.Context.Device); + + // + // Extract UserSelectByParam from XActionRequest ... + var userSelectByParam = GetUserSelectByParam(request); + + // + // Validate Request Pointer User doesn't Exists ... + await ValidateUserNotExists(userSelectByParam); + + // + // Create an Empty Instance of XUser ... + var xUser = new XUser(); + + // + // Start Filling User Fields ... + xUser.UserName = userName; + xUser.Email = request.Context.Email; + xUser.PhoneNumber = request.Context.MobileNumber; + + // + xUser.FirstName = request.Context.FirstName; + xUser.LastName = request.Context.LastName; + xUser.DateOfBirth = request.Context.DateOfBirth; + + // + xUser.EmailConfirmed = request.Context.EmailVerified; + xUser.PhoneNumberConfirmed = request.Context.MobileVerified; + + // + xUser.CreationDate = DateTime.UtcNow; + xUser.LastLogin = null; + + // + #region Handling New User IsEnable State ... + // + // By Default New Users will Enables ... + xUser.IsEnable = true; + xUser.IsBanned = false; + + // + // Check Auto Enable Users ... + if (Configuration.RequireRegistrationConfirm) + { + xUser.IsEnable = false; + } + + // + // Check Auto Confirm Email Users ... + if (Configuration.AutoConfirmNewUsersEmail) + { + xUser.EmailConfirmed = true; + } + #endregion + + // + // Check Auto Confirm Phone Number Users ... + if (Configuration.AutoConfirmNewUsersPhoneNumber) + { + xUser.PhoneNumberConfirmed = true; + } + + // + // Validating given Password ... + await ValidatePasswordPolicies(xUser, password); + + // + // Now add new User to Database ... + var createUserResult = await CreateUserAsync(xUser, password); + if (!createUserResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + // Update Request UserId Value ... + request.Context.UserId = xUser.Id; + request.Context.UserName = xUser.UserName; + + // + #region Handling New User Role ... + // + // Now Try to Assign Default User role ... + var isContainsDefaultUserRole = !Configuration.NewUsersRole.IsNullOrEmpty(); + if (isContainsDefaultUserRole) + { + // + var newUserRoleName = Configuration.NewUsersRole; + var isNewUserRoleExists = await IsRoleExistsAsync(newUserRoleName); + // + // Make Sure New User role Exists ... + if (!isNewUserRoleExists) + { + // + // Create Default Roles ... + await CreateIdentityRoles(); + } + + // + // Assign User to Role ... + var assignNewUserToRoleResult = await AddUserToRoleAsync(xUser, newUserRoleName); + if (!assignNewUserToRoleResult.Succeeded) + { + // + // Delete User if Role Assignment Faild ... + await UserManager.DeleteAsync(xUser); + + // + // Thrown ActionFailed Error ... + XException.ActionFailed.Throw(); + } + } + #endregion + + // + #region Handle Profile Image ... + // Save and Attach Profile Image to User if it's Added Before ... + if (!request.Context.UserId.IsNullOrEmpty() && + !request.Context.Thubmnail.IsNull()) + { + // + xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + exception: XException.NotFound.ToException() + ); + + // + // Handle Saving File and Attach it to User ... + var saveResult = await StorageProvider + .HandleProfileImageSave(request.Context.Thubmnail); + + // + var xProfileImage = new XProfileImage + { + UserId = request.Context.UserId, + Name = saveResult.FileName, + Path = saveResult.FilePath, + Thumb = saveResult.Thmbnail, + ThumbPath = saveResult.ThmbnailPath + }; + + // + xUser.Avatars.Add(xProfileImage); + var updateResult = await UpdateUserAsync( + xUser, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + if (updateResult.Succeeded) + { + // + xUser.Avatar = xProfileImage.ThumbPath; + await UpdateUserAsync( + xUser, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + } + } + #endregion + + // + // Handle Attaching User device ... + var xUserDevice = await AddUserRelatedDevice( + userSelectByParam, + device, + checkCanLoginPolicies: false); + + // + // Prepare New Token Result Resource, + // by Requesting an Action ... + var result = await ActionRequest( + lang, + device, + userSelectByParam, + XAction.Finish, + request.Context, + forceCheckUserExists: false + ); + + // + // Remove Previous Token ... + await RemoveTokenByHash(actionToken); + + // + // Return Result ... + return result; + } + + /// + /// Attach Profile Image + /// + /// a token which approved user action + /// an instance of IFormFile for user's Avatar + /// an instance of XActionResponse + public async Task AttachProfileImage( + string actionToken, + IFormFile file + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(actionToken) + .AddNotNull(file) + .ValidateGroupAsync(); + + // + // Get Related Token to Registration Hash and Validate it, + // then Parse XActionRequest Instance from Token ... + var request = await ValidateAndParseActionHash(actionToken); + + // + // Extract UserSelectByParam from XActionRequest ... + var userSelectByParam = GetUserSelectByParam(request); + if (userSelectByParam.IsNullOrEmpty()) + { + XException.InvalidData.Throw(); + } + + // + // Validate Given FormFile as Profile Image ... + StorageProvider.ValidateProfileImage(file); + + // + request.Context.Thubmnail = file; + + // + // Save and Attach Profile Image to User if it's Added Before ... + if (!request.Context.UserId.IsNullOrEmpty()) + { + // + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + exception: XException.NotFound.ToException() + ); + + // + // Handle Saving File and Attach it to User ... + var saveResult = await StorageProvider.HandleProfileImageSave(file); + + // + var xProfileImage = new XProfileImage + { + UserId = request.Context.UserId, + Name = saveResult.FileName, + Path = saveResult.FilePath, + Thumb = saveResult.Thmbnail, + ThumbPath = saveResult.ThmbnailPath + }; + + // + xUser.Avatars.Add(xProfileImage); + var updateResult = await UpdateUserAsync( + xUser, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + + // + if (updateResult.Succeeded) + { + // + xUser.Avatar = xProfileImage.ThumbPath; + await UpdateUserAsync( + xUser, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + } + } + + // + // Remove Invitation Token ... + if (!actionToken.IsNullOrEmpty()) + { + await RemoveTokenByHash(actionToken); + } + + // + var result = await ActionRequest( + request.Context.Lang, + request.Context.Device, + userSelectByParam, + XAction.ProfileAction, + request.Context, + forceCheckUserExists: false + ); + + // + // Return Result ... + return result; + } + + /// + /// Finishing Registration + /// + /// specify destination language + /// an instance of XDevice + /// user identifier + /// assigne password + /// return url for invitation user to redirect + /// + public async Task FinishRegistration( + string lang, + XDevice device, + string userSelectByParam, + string password, + string returnUrl + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(lang, userSelectByParam, password, returnUrl) + .AddNotNull(device) + .AddUrl(returnUrl) + .ValidateGroupAsync(); + + // + // Validate User Exists And Retrieve it ... + var xUser = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + checkIsBanned: false, + ignoreDisabledUser: true, + forceAdmin: false + ); + + // + #region Handling New User Email Notification Senario ... + // + // Here we Check if User is Enable and Approved Email + // send RegisteredMsg and if User is not Enable and + // Verify Registration value in Configuration is True + // send Verify Registration mail to user and handle it ... + if (Configuration.RequireRegistrationConfirm) + { + // + if (xUser.IsEnable) + { + // + // Send Registration Finished Message ... + // since in this step we need to prevent errors from + // going forward. add this try catch here ... + try + { + // + var xMessage = GetRegistrationFinishedMessage(lang, xUser.Email, returnUrl); + + // + await MessageProvider.SendMailAsync(xMessage); + } + catch { } + } + else + { + // + // Send Registration Confirm Message ... + // since in this step we need to prevent errors from + // going forward. add this try catch here ... + try + { + // + var request = await ActionRequest( + lang, + device, + userSelectByParam, + XAction.Finish, + forceCheckContext: false, + forceCheckUserExists: true); + + // + var xMessage = GetRegistrationConfirmMessage(lang, xUser.Email, request.Token, returnUrl); + + // + await MessageProvider.SendMailAsync(xMessage); + } + catch { } + } + } + + // // + // await RemoveTokenByDevice (device); + #endregion + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Request.cs + +using System.Collections.Generic; +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIdentityModels.Navigations; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Action Handlers ... + /// + /// Request an Action + /// + /// specify destination language + /// an instance of XDevice + /// specified user's identifier + /// a member of XAction which represent Action Step + /// an instance of XActionRequestContext which provides required informations for specified step + /// specify checking context, default is false + /// specify check user exists, default is true + /// specify check user and device relation, default is false + /// specifies force renew Action Token if it's expired, default is false + /// an instance of XActionResponse + private async Task ActionRequest( + string lang, + XDevice device, + string userSelectByParam, + XAction step, + XActionRequestContext context = null, + bool forceCheckContext = false, + bool forceCheckUserExists = true, + bool forceCheckUserDevice = false, + bool forceRenewToken = false + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(lang); + ValidationProvider.NotNull(device); + + // + // Check Device not Banned ... + await ValidateDeviceForActions(device); + + // + // Check User Exists and Device Relation ... + if (forceCheckUserExists) + { + if (forceCheckUserDevice) + { + // + if (context.Device != null && + !device.IsSameAs(context.Device)) + { + XException.InvalidDevice.Throw(); + } + + // + await ValidateUserAndDeviceRelation(userSelectByParam, device); + } + else + { + await ValidateUserExistsAsync(userSelectByParam); + } + } + + // + XToken xToken = null; + XActionResponse result = null; + XActionRequestToken xRequest = null; + + // + var isExists = false; + var forceUserSelectByParamNotEmpty = true; + switch (step) + { + case XAction.Registration: + // + forceUserSelectByParamNotEmpty = false; + isExists = await IsTokenExistsByDeviceAndType(device, step); + + // + if (isExists) + { + xToken = await ValidateAndRetieveTokenByDeviceAndType(device, step); + } + break; + + default: + // + isExists = await IsTokenExistsByUserAndType(userSelectByParam, step); + + // + if (isExists) + { + xToken = await ValidateAndRetieveTokenByUserAndType(userSelectByParam, step); + } + break; + } + + // + if (isExists) + { + // + var isValidToken = IsValidToken(xToken.Token); + if (!isValidToken && !forceRenewToken) + { + switch (step) + { + case XAction.Registration: + case XAction.Invite: + forceRenewToken = true; + break; + + default: + throw XException.InvalidToken.ToException(); + } + } + + // + if (!isValidToken && forceRenewToken) + { + await ValidateAndHandleToken(xToken.Token); + return await ActionRequest( + lang, + device, + userSelectByParam, + step, + context, + forceCheckContext, + forceCheckUserExists, + forceCheckUserDevice, + forceRenewToken); + } + + // + xRequest = ValidateAndParseActionToken(xToken.Token); + + // + // Check Exists Context ... + if (forceCheckContext) + { + // + var isSameContext = xRequest.Context.IsSameAs(context); + if (!isSameContext) + { + XException.InvalidData.Throw(); + } + } + } + else + { + // + if (context != null) + { + // + var contextUserSelectByParam = GetUserSelectByParam( + context, + false, + new List { + XUserSelectBy.ID, + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + var contextLang = context.Lang; + var contextDevice = context.Device; + + // + // prevent from issues on Registration Request ... + if (forceUserSelectByParamNotEmpty) + { + var userSelectByType = GetUserSelectByType(userSelectByParam); + var contextUserSelectByType = GetUserSelectByType(contextUserSelectByParam); + if (contextUserSelectByType != userSelectByType) + { + // + var excludesList = GenerateUserSelectByExcludes(new List { userSelectByType }); + + // + contextUserSelectByParam = GetUserSelectByParam(context, false, excludesList); + } + } + + // + // Fix Device Not Same on Invitation and Request + if (step == XAction.Registration) + { + // + context.Device = device; + contextDevice = context.Device; + } + + // + // Validate Context Data must be Same as Request Data ... + if ((!contextUserSelectByParam.IsNullOrEmpty() && + contextUserSelectByParam != userSelectByParam) || + (!contextLang.IsNullOrEmpty() && + contextLang != lang) || + (contextDevice != null && + !contextDevice.IsSameAs(device))) + { + XException.InvalidData.Throw(); + } + } + else + { + // + context = new XActionRequestContext + { + Lang = lang, + Device = device + }; + + // + // Add Content ... + var userSelectByType = GetUserSelectByType( + userSelectByParam, + forceUserSelectByParamNotEmpty); + switch (userSelectByType) + { + case XUserSelectBy.MobileNumber: + context.MobileNumber = userSelectByParam; + break; + + case XUserSelectBy.Email: + context.Email = userSelectByParam; + break; + + case XUserSelectBy.Username: + context.UserName = userSelectByParam; + break; + + case XUserSelectBy.ID: + context.UserId = userSelectByParam; + break; + + case XUserSelectBy.NotSpecified: + default: + if (forceCheckUserExists) + { + XException.InvalidArgs.Throw(); + } + break; + } + } + + // + // Generate Registration Request ... + xRequest = new XActionRequestToken + { + Action = step, + Context = context + }; + + // + // Add User Id if Exists to Context ... + var isUserExists = await IsUserExistsAsync(userSelectByParam); + if (isUserExists) + { + // + var user = await GetUserAsync(userSelectByParam); + if (user != null) + { + xRequest.Context.UserId = user.Id; + } + } + + // + xRequest.Prepare(Configuration.IdentitySecretKey); + + // + xToken = await HashRequest( + xRequest, + forceUserSelectByParamNotEmpty); + } + + // + // When XToken Exists Passed XToken ... + result = GetActionResultResponse(xToken); + + // + if (result == null) + { + XException.ActionFailed.Throw(); + } + + // + return result; + } + + /// + /// Request a Mobile Verification Code + /// + /// specify destination language + /// an instance of XDevice + /// a token which approved user action + /// the mobile number which is going to request a verification code + /// check mobile number is in use or not, default is true + /// an instance of XActionResponse + private async Task RequestMobileVerificationCode( + string lang, + XDevice device, + string actionToken, + string mobileNumber, + bool checkMobileInUse = true + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(lang, actionToken, mobileNumber) + .AddMobileNumber(mobileNumber) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + // Check Device Validation ... + await ValidateDeviceForActions(device); + + // + // Check Mobile is Unique ... + if (checkMobileInUse) + { + var isUserExists = await IsUserExistsAsync(mobileNumber); + if (isUserExists) + { + // + var xUser = await GetUserAsync(mobileNumber); + + // + if (xUser.PhoneNumberConfirmed) + { + XException.MobileInUsed.Throw(); + } + } + } + + // + // Get Related Token to Registration Hash and Validate it, + // then Parse XActionRequest Instance from Token ... + var request = await ValidateAndParseActionHash(actionToken); + + // + // Validate two Device Must Same ... + ValidateRequestAndGiveDevices(device, request.Context.Device); + + // + // Extract UserSelectByParam from XActionRequest ... + var userSelectByParam = GetUserSelectByParam( + request, + forceNotNull: false, + excludes: new List { + XUserSelectBy.ID, + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + + // + // if it's null, means user doesn't have Invitation Token + // and there is no User Selector ... + if (userSelectByParam.IsNullOrEmpty()) + { + // + // Since Mobile is Unique so it can act as a + // UserSelector ... + userSelectByParam = mobileNumber; + } + + // + // Define empty Verification Code + // and Empty Verification Request ... + var verificationCode = ""; + XVerificationRequest xVerificationRequest = null; + + // + // Check Device Request Verification Code before or Not ... + var isDeviceRequested = await IsDeviceRequestedForVerificationCodeBefor(device); + if (isDeviceRequested) + { + // + // if Requested before, Retrieve XVerification instance by Device from Db + // and Validate it ... + xVerificationRequest = await ValidateAndRetrieveVerificationRequest(device); + + // + if (xVerificationRequest.VerificationCode.IsNullOrEmpty()) + { + // + verificationCode = GenerateRandom().ToString(); + xVerificationRequest.VerificationCode = verificationCode; + + // + await RemoveVerificationCodeRequestByDevice( + device, + saveChanges: false + ); + await AddVerificationRequest( + xVerificationRequest, + saveChanges: false + ); + + // + await DbContext.SaveChangesAsync(); + } + + // + // if XVerificationRequest is Valid, it must Contain an Alive Verification Code + // so retrieve it and assign it ... + verificationCode = xVerificationRequest.VerificationCode; + } + else + { + // + verificationCode = GenerateRandom().ToString(); + } + + // + // if it's First Verification Request of given Device + // Generate a new Verification Code ... + if (verificationCode.IsNullOrEmpty()) + { + verificationCode = GenerateRandom().ToString(); + } + + // + // Check Context Mobile and given Mobile ... + if (!request.Context.MobileNumber.IsNullOrEmpty() && + mobileNumber != request.Context.MobileNumber) + { + XException.InvalidMobileNumber.Throw(); + } + + // + // Check User Added To Db or Not ... + var isAddedUser = !request.Context.UserId.IsNullOrEmpty(); + if (isAddedUser) + { + // + // Validate User Exists and Retireve User Object ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + checkIsBanned: false); + + // + // Check Mobiles Same ... + if (user.PhoneNumber == mobileNumber && + user.PhoneNumberConfirmed) + { + XException.MobilesSame.Throw(); + } + + // + // Update User Object's Mobile Number and + // Confirmation Status ... + user.PhoneNumber = mobileNumber; + user.PhoneNumberConfirmed = false; + + // + // Save Changes to Db ... + await UpdateUserAsync(user, checkCanLoginPolicies: false, checkIsBanned: false); + } + + // + // Prepare Verification Request by Checking Next Tries, + // Device Status, and etc, Update or Add XVerificationRequest to db + // and Retrieve the added Entity ... + xVerificationRequest = await HandleVerificationRequestPreparation(verificationCode, request); + + // + // Update Request Context MobileNumber + // and it's Confirmation Status ... + request.Context.MobileNumber = mobileNumber; + request.Context.MobileVerified = false; + + // + // Remove Previous Token ... + await RemoveTokenByHash(actionToken); + + // + // Prepare New Token Result Resource, + // by Requesting an Action ... + var result = await ActionRequest( + lang, + device, + userSelectByParam, + XAction.RequestMobileVerificationCode, + request.Context, + forceCheckUserExists: isAddedUser + ); + + // + // Prepare Message ... + var xMessage = GetVerificationCodeMessage(lang, mobileNumber, verificationCode); + + // + // Send Verification Code Message to User Using SMS Provider ... + await MessageProvider.SendSmsAsync(xMessage, throwException: false); + + // + // Return Result ... + return result; + } + + /// + /// Request a Email Verification Code + /// + /// specify destination language + /// an instance of XDevice + /// a token which approved user action + /// the email address which is going to request a verification code + /// check email address is in use or not, default is true + /// an instance of XActionResponse + public async Task RequestEmailVerificationCode( + string lang, + XDevice device, + string actionToken, + string emailAddress, + bool checkEmailInUse = true + ) + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotEmpty(lang, actionToken, emailAddress) + .AddEmailAddress(emailAddress) + .AddNotNull(device) + .ValidateGroupAsync(); + + // + // Check Device Validation ... + await ValidateDeviceForActions(device); + + // + // Check Email is Unique ... + if (checkEmailInUse) + { + var isUserExists = await IsUserExistsAsync(emailAddress); + if (isUserExists) + { + // + var xUser = await GetUserAsync(emailAddress); + + // + if (xUser.EmailConfirmed) + { + XException.EmailInUsed.Throw(); + } + } + } + + // + // Get Related Token to Registration Hash and Validate it, + // then Parse XActionRequest Instance from Token ... + var request = await ValidateAndParseActionHash(actionToken); + + // + // Validate two Device Must Same ... + ValidateRequestAndGiveDevices(device, request.Context.Device); + + // + // Extract UserSelectByParam from XActionRequest ... + var userSelectByParam = GetUserSelectByParam( + request, + forceNotNull: false, + excludes: new List { + XUserSelectBy.ID, + XUserSelectBy.Email, + XUserSelectBy.MobileNumber + }); + + // + // if it's null, means user doesn't have Invitation Token + // and there is no User Selector ... + if (userSelectByParam.IsNullOrEmpty()) + { + // + // Since Email is Unique so it can act as a + // UserSelector ... + userSelectByParam = emailAddress; + } + + // + // Define empty Verification Code + // and Empty Verification Request ... + var verificationCode = ""; + XVerificationRequest xVerificationRequest = null; + + // + // Check Device Request Verification Code before or Not ... + var isDeviceRequested = await IsDeviceRequestedForVerificationCodeBefor(device); + if (isDeviceRequested) + { + // + // if Requested before, Retrieve XVerification instance by Device from Db + // and Validate it ... + xVerificationRequest = await ValidateAndRetrieveVerificationRequest(device); + + // + if (xVerificationRequest.VerificationCode.IsNullOrEmpty()) + { + // + verificationCode = GenerateRandom().ToString(); + xVerificationRequest.VerificationCode = verificationCode; + + // + await RemoveVerificationCodeRequestByDevice( + device, + saveChanges: false + ); + await AddVerificationRequest( + xVerificationRequest, + saveChanges: true + ); + } + + // + // if XVerificationRequest is Valid, it must Contain an Alive Verification Code + // so retrieve it and assign it ... + verificationCode = xVerificationRequest.VerificationCode; + } + else + { + // + verificationCode = GenerateRandom().ToString(); + } + + // + // Check Context Email and given Email ... + if (!request.Context.Email.IsNullOrEmpty() && + emailAddress != request.Context.Email) + { + XException.InvalidEmailAddress.Throw(); + } + + // + // Check User Added To Db or Not ... + var isAddedUser = !request.Context.UserId.IsNullOrEmpty(); + if (isAddedUser) + { + // + // Validate User Exists and Retireve User Object ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: false, + checkIsBanned: false); + + // + // Check Email Same ... + if (user.Email == emailAddress && + user.EmailConfirmed) + { + XException.MobilesSame.Throw(); + } + + // + // Update User Object's Email and + // Confirmation Status ... + user.Email = emailAddress; + user.EmailConfirmed = false; + + // + // Save Changes to Db ... + await UpdateUserAsync( + user, + checkCanLoginPolicies: false, + checkIsBanned: false + ); + } + + // + // Prepare Verification Request by Checking Next Tries, + // Device Status, and etc, Update or Add XVerificationRequest to db + // and Retrieve the added Entity ... + xVerificationRequest = await HandleVerificationRequestPreparation(verificationCode, request); + + // + // Update Request Context Email + // and it's Confirmation Status ... + request.Context.Email = emailAddress; + request.Context.EmailVerified = false; + + // + // Prepare New Token Result Resource, + // by Requesting an Action ... + var result = await ActionRequest( + lang, + device, + userSelectByParam, + XAction.RequestEmailVerificationCode, + request.Context, + forceCheckUserExists: isAddedUser + ); + + // + // Remove Hash ... + await RemoveTokenByHash(actionToken); + + // + // Prepare Message ... + var xMessage = GetVerificationCodeMessage(lang, emailAddress, verificationCode); + + // + // Send Verification Code Message to User Using Mail Provider ... + await MessageProvider.SendMailAsync(xMessage, throwException: false); + + // + // Return Result ... + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Role.cs + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Role Actions ... + /// + /// Check a Role exists or not + /// + /// role name + /// a boolean value + public async Task IsRoleExistsAsync( + string roleName + ) + { + // + if (roleName.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + return await RoleManager.RoleExistsAsync(roleName); + } + + /// + /// Create a new Role + /// + /// role name + /// an instance of IdentityResult + public async Task CreateRoleAsync( + string roleName + ) + { + // + var isExists = await RoleManager + .RoleExistsAsync(roleName); + if (isExists) + { + XException.Duplicate.Throw(); + } + + // + var result = await RoleManager + .CreateAsync(new IdentityRole(roleName)); + + // + return result; + } + + /// + /// Get a Role + /// + /// role name + /// an instance of IdentityRole + public async Task GetRoleAsync( + string roleName + ) + { + // + var isExists = await IsRoleExistsAsync(roleName); + if (!isExists) + { + XException.NotFound.Throw(); + } + + // + var result = await RoleManager + .FindByNameAsync(roleName); + + // + return result; + } + + /// + /// Remove a User From Role + /// + /// an instance of XUser + /// role name + /// an instance of IdentityResult + public async Task RemoveFromRoleAsync( + XUser user, + string roleName + ) + { + return await UserManager.RemoveFromRoleAsync(user, roleName); + } + + /// + /// Remove a User From Roles + /// + /// an instance of XUser + /// a collection of role names + /// an instance of IdentityResult + public async Task RemoveFromRolesAsync( + XUser user, + IEnumerable roleNames + ) + { + return await UserManager.RemoveFromRolesAsync(user, roleNames); + } + + /// + /// Assign a User to Specific Role + /// + /// an instance of XUser + /// role name + /// an instance of IdentityResult + public async Task AddUserToRoleAsync( + XUser user, + string roleName + ) + { + // + if (user == null || + roleName.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + var userSelectByParam = GetUserSelectByParam(user); + var isExistsUser = await IsUserExistsAsync(userSelectByParam); + var isExistsRole = await IsRoleExistsAsync(roleName); + if (!isExistsUser || !isExistsRole) + { + XException.NotFound.Throw(); + } + + // + var result = await UserManager + .AddToRoleAsync(user, roleName); + + // + return result; + } + + /// + /// Retrieve Role Name by it's ID + /// + /// role id + /// role name as string + public async Task GetRoleNameAsync( + string roleId + ) + { + // + // Validate Args ... + if (roleId.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + var role = await RoleManager + .FindByIdAsync(roleId); + + // + return role.Name.ToNormalString(); + } + + /// + /// Get a List Of User Roles + /// + /// user identifier + /// + /// + /// a collection of role names + public async Task> GetRoleNamesAsync( + string userSelectByParam, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ) + { + // + // Validate Args ... + if (userSelectByParam.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: checkCanLoginPolicies, + checkIsBanned: checkIsBanned + ); + + // + var result = new List(); + foreach (var role in user.Roles) + { + // + var roleName = await GetRoleNameAsync(role.RoleId); + roleName = roleName.ToNormalString(); + + // + result.Add(roleName); + } + + // + return result; + } + + /// + /// Check a User is Adminr not + /// + /// user identifier + /// a boolean value + public async Task IsAdmin( + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true, + checkCanLoginPolicies: true); + + // + var result = await IsAdmin(user); + + // + return result; + } + + /// + /// Check a User is Adminr not + /// + /// an instance of XUser + /// a boolean value + public async Task IsAdmin( + XUser user + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(user); + ValidationProvider.NotEmpty(user.Id); + + // + // Retrieve Admin Role ... + // TODO: Fix this ... + var adminRole = "admin"; + // XUserRole.Admin + // .GetStringValue () + // .ToNormalString (); + + // + // Check Admin Role Exists in User Roles or not ... + var userRoleNames = await GetRoleNamesAsync(user.Id); + var isContainsAdminRole = userRoleNames.Contains(adminRole); + + // + // Return Result ... + return isContainsAdminRole; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Search.cs + +using System.Threading.Tasks; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Dtos; +using xModels.Dtos; +using xDataService.Extensions; +using xIds.Extensions; +using System.Linq; +using xIdentityModels.Extensions; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Actions ... + /// + /// Query User Profiles + /// + /// requested user's identifier + /// how to filter results based on XQuery structure + /// an instance of XQueryResult of XUserProfileDto + public async Task> QueryOpenToSearchUsers( + string requestedUserSelectByParam, + XQuery query + ) + { + // + // Validation ... + if (query.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + query = query.NormalizeQuery(DataConfiguration.ToXDataServiceConfig()); + + // + // Since in Resourceable Entities we have to Search on Locales + // we Must Implement Senario Custom ... + var totalEntities = GetUsersDbSet() + .Where(u => u.OpenToSearch) + .ToList() + .Where(u => !u.ContainsUserSelectByParam(requestedUserSelectByParam)) + .Select(u => u.Id) + .ToList(); + + // + var items = await GetUserProfilesAsync( + totalEntities, + requestedUserSelectByParam, + checkIsBanned: false, + checkCanLoginPolicies: false, + forceCheckRequestedUser: false + ); + + // + 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 + ); + } + + // + // Generate Result Object ... + // Query = query, + var result = new XQueryResult + { + Items = items, + Page = query.Page, + PageSize = query.PageSize, + TotalPages = totalPagesCount, + TotalItems = totalItemsCount, + TotalFilteredPages = filteredPagesCount, + TotalFilteredItems = filteredItemsCount + }; + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Seed.cs + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Descriptors; +using xIdentityModels.Extensions; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Default Identity Preparation Actions ... + /// + /// Create Default Roles based on Identity Configuration + /// + /// + public async Task CreateIdentityRoles() + { + // + if (Configuration == null || + Configuration.IdentityRoles == null || + Configuration.IdentityRoles.Count == 0) + { + return; + } + + // + // Check New Users Role exists in IdentityRole or not ... + if (!Configuration.NewUsersRole.IsNullOrEmpty()) + { + // + // Ensure NewUsersRole Exists in IdentityRoles ... + var isNewUserRoleExistsInIdentityRoles = Configuration + .IdentityRoles + .Contains(Configuration.NewUsersRole); + if (!isNewUserRoleExistsInIdentityRoles) + { + XException.InvalidArgs.Throw(); + } + } + + // + // loop through all roles and create them one by one ... + foreach (var roleName in Configuration.IdentityRoles) + { + // + var isRoleExists = await RoleManager.RoleExistsAsync(roleName); + if (!isRoleExists) + { + // + var result = await RoleManager.CreateAsync(new IdentityRole(roleName)); + } + } + } + + /// + /// Create a user Based on User Descriptor + /// + /// an instance of XIdentityUserDescriptor + /// + public async Task CreateUserAsync( + XIdentityUserDescriptor user + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(user); + + // + var isExistsRole = await RoleManager.RoleExistsAsync(user.Role); + if (!isExistsRole) + { + // + // Check Assigned role to user exists in app roles ... + if (!Configuration + .IdentityRoles + .Contains(user.Role)) + { + XException.InvalidArgs.Throw(); + } + + // + // Ensure All Roles Created ... + await CreateIdentityRoles(); + } + + // + // Check User Exists ... + var isExistsUser = await IsUserExistsAsync(user.UserName); + if (!isExistsUser) + { + // + // Create a DashboardUser instance based on Configuration Data ... + var userEntity = user.ToXUser(); + + // + // Try to Create User ... + var userCreateResult = await CreateUserAsync( + userEntity, + user.Password + ); + if (!userCreateResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + // Assign Admin User to it's Role ... + var roleAssignResult = await UserManager.AddToRoleAsync(userEntity, user.Role); + if (!roleAssignResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + + // + var claims = await ToJwtClaims( + userEntity, + checkCanLoginPolicies: true, + checkIsBanned: true); + + // + var addClaimsResult = await UserManager.AddClaimsAsync(userEntity, claims); + if (!addClaimsResult.Succeeded) + { + XException.ActionFailed.Throw(); + } + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Token.cs + +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using xCommons.Extensions; +using xCommons.Helpers; +using xExceptions.Constants; +using xIdentityModels; +using xIdentityModels.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIdentityModels.Navigations; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Token Actions ... + /// + /// Check a Token Entity Exists or not + /// + /// an instance of XDevice + /// specifies Action Step by a member of XAction + /// a boolean value + private async Task IsTokenExistsByDeviceAndType( + XDevice device, + XAction type + ) + { + // + ValidationProvider.NotNull(device); + + // + var isExists = false; + var enumerator = DbContext.Tokens.AsAsyncEnumerable(); + await + foreach (var token in enumerator) + { + // + if (token.Type == type && + token.Device.IsSameAs(device)) + { + isExists = true; + break; + } + } + + // + return isExists; + } + + /// + /// Check a Token Entity Exists or not + /// + /// specified user's identifier + /// specifies Action Step by a member of XAction + /// a boolean value + private async Task IsTokenExistsByUserAndType( + string userSelectByParam, + XAction type + ) + { + // + ValidationProvider.NotEmpty(userSelectByParam); + + // + var isExists = false; + var tokensEnumerator = DbContext.Tokens.AsAsyncEnumerable(); + await + foreach (var token in tokensEnumerator) + { + // + if (token.Type == type && + token.UserSelectByParam + .ToNormalString() == userSelectByParam + .ToNormalString()) + { + // + isExists = true; + break; + } + } + + // + return isExists; + } + + /// + /// Check a Token Entity Exists or not + /// + /// represent specified action token + /// a boolean value + private async Task IsTokenExistsByToken( + string token + ) + { + // + var result = await DbContext.Tokens + .AnyAsync(xt => xt.Token == token); + + // + return result; + } + + /// + /// Check a Token Entity Exists or not + /// + /// represent specified action token hash + /// a boolean value + private async Task IsTokenExistsByHash( + string hash + ) + { + // + var result = await DbContext.Tokens + .AnyAsync(xt => xt.Hash == hash); + + // + return result; + } + + /// + /// Check a Token Entity Exists or not + /// + /// an instance of XToken + /// a boolean value + private async Task IsTokenExistsByToken( + XToken item + ) + { + // + var result = false; + var tokenEnumerator = DbContext.Tokens + .AsAsyncEnumerable(); + await + foreach (var token in tokenEnumerator) + { + // + if (token.Hash == item.Hash && + token.Token == item.Token && + token.Type == item.Type && + token.UserSelectByParam + .ToNormalString() == item.UserSelectByParam + .ToNormalString() && + token.Device.IsSameAs(item.Device)) + { + // + result = true; + break; + } + } + + // + return result; + } + + /// + /// Check a Token Entity Exists or not + /// + /// specifies token id + /// a boolean value + private async Task IsTokenExistsById( + int id + ) + { + return await DbContext.Tokens.AnyAsync(t => t.Id == id); + } + + /// + /// Retrieve Token Entity + /// + /// an instance of XDevice + /// specifies Action Step by a member of XAction + /// an instance of XToken + private async Task GetTokenByDeviceAndType( + XDevice device, + XAction type + ) + { + // + var isExists = await IsTokenExistsByDeviceAndType(device, type); + if (!isExists) + { + return null; + } + + // + XToken item = null; + var enumerator = DbContext.Tokens.AsAsyncEnumerable(); + await + foreach (var token in enumerator) + { + // + if (token.Type == type && + token.Device.IsSameAs(device)) + { + // + item = token; + break; + } + } + + // + return item; + } + + /// + /// Retrieve Token Entity + /// + /// specified user's identifier + /// specifies Action Step by a member of XAction + /// an instance of XToken + private async Task GetTokenByUserAndType( + string userSelectByParam, + XAction type + ) + { + // + var isExists = await IsTokenExistsByUserAndType(userSelectByParam, type); + if (!isExists) + { + return null; + } + + // + XToken item = null; + var enumerator = DbContext.Tokens.AsAsyncEnumerable(); + await + foreach (var token in enumerator) + { + // + if (token.Type == type && + token.UserSelectByParam + .ToNormalString() == userSelectByParam + .ToNormalString()) + { + // + item = token; + break; + } + } + + // + return item; + } + + /// + /// Retrieve a Token Entity + /// + /// represent specified action token + /// an instance of XToken + private async Task GetTokenByToken( + string token + ) + { + // + var isExists = await IsTokenExistsByToken(token); + if (!isExists) + { + return null; + } + + // + var result = await DbContext.Tokens + .FirstOrDefaultAsync(xt => xt.Token == token); + + // + return result; + } + + /// + /// Retrieve Related Hash to a Token + /// + /// represent specified action token + /// hash string + private async Task GetRelatedHashByToken( + string token + ) + { + // + var item = await GetTokenByToken(token); + if (item == null) + { + return null; + } + + // + return item.Hash; + } + + /// + /// Retrieve a Token Entity + /// + /// represent specified action token hash + /// an instance of XToken + private async Task GetTokenByHash( + string hash + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(hash); + + // + var isExists = await IsTokenExistsByHash(hash); + if (!isExists) + { + return null; + } + + // + var item = await DbContext.Tokens + .FirstOrDefaultAsync(t => t.Hash == hash); + + // + return item; + } + + /// + /// Retrieve Related Token to a Hash string + /// + /// represent specified action token hash + /// token string + private async Task GetRelatedTokenByHash( + string hash + ) + { + // + var item = await GetTokenByHash(hash); + if (item == null) + { + return null; + } + + // + return item.Token; + } + + /// + /// Retrieve a Token Expiration Date + /// + /// represent specified action token + /// an instance of DateTime + private DateTime GetTokenExpirationDate( + string token + ) + { + // + ValidationProvider.NotEmpty(token); + + // + var result = IdentityHelper.GetTokenExpirationDate(token); + + // + return result; + } + + /// + /// Remove a Token Entity + /// + /// represent specified action token + /// + private async Task RemoveTokenByToken( + string token + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(token); + + // + var isExists = await IsTokenExistsByToken(token); + if (!isExists) + { + return; + } + + // + var item = await GetTokenByToken(token); + if (item == null) + { + return; + } + + // + DbContext.Tokens.Remove(item); + await DbContext.SaveChangesAsync(); + } + + /// + /// Remove a Token Entity + /// + /// represent specified action token hash + /// + private async Task RemoveTokenByHash( + string hash + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(hash); + + // + var isExists = await IsTokenExistsByHash(hash); + if (!isExists) + { + return; + } + + // + var item = await GetTokenByHash(hash); + if (item == null) + { + return; + } + + // + DbContext.Tokens.Remove(item); + await DbContext.SaveChangesAsync(); + } + + /// + /// Remove a Token Entity + /// + /// an instance of XDevice + /// + private async Task RemoveTokenByDevice( + XDevice device + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(device); + + // + XToken items = null; + var enumerator = DbContext.Tokens.AsAsyncEnumerable(); + await + foreach (var token in enumerator) + { + // + if (token.Device.IsSameAs(device)) + { + // + items = token; + break; + } + } + + // + if (!items.IsNull()) + { + DbContext.Tokens.RemoveRange(items); + await DbContext.SaveChangesAsync(); + } + } + + /// + /// add a token Entity + /// + /// an instance of XToken + /// an instance of XToken + private async Task AddTokenAsync( + XToken item + ) + { + // + var isExist = await IsTokenExistsByToken(item); + if (isExist) + { + return await GetTokenByHash(item.Hash); + } + + // + var entry = await DbContext.Tokens.AddAsync(item); + await DbContext.SaveChangesAsync(); + + // + return entry.Entity; + } + + /// + /// Hash a Request Token + /// + /// an instance of XActionRequestToken + /// + /// + /// an instance of XToken + private async Task HashRequest( + XActionRequestToken request, + bool forceNotNullUserSelectByParam = true, + bool forceDeviceNotNull = true + ) + { + // + // Validate Args ... + var secretKey = Configuration.IdentitySecretKey; + ValidationProvider.NotNull(request); + ValidationProvider.NotEmpty(secretKey); + + // + ValidateActionRequest(request); + + // + var type = request.Action; + var device = request.Context.Device; + var tokenObject = ToSecurityToken(request); + var tokenString = ToTokenString(tokenObject); + var tokenHash = ToHash(tokenString); + var userSelectByParam = GetUserSelectByParam(request, forceNotNullUserSelectByParam); + + // + if (forceNotNullUserSelectByParam) + { + ValidationProvider.NotEmpty(userSelectByParam); + } + + // + ValidationProvider.NotEmpty(tokenString, tokenHash); + + // + if (forceDeviceNotNull) + { + ValidationProvider.NotNull(device); + } + + // + ValidationProvider.NotNull(tokenObject); + + // + var xToken = new XToken + { + Type = type, + Device = device, + Hash = tokenHash, + Token = tokenString, + UserSelectByParam = userSelectByParam + }; + + // + var addedToken = await AddTokenAsync(xToken); + + // + return addedToken; + } + + /// + /// Generate Token Hash string + /// + /// represent specified action token + /// check token validation parameters, default is true + /// hash string + private string ToHash( + string token, + bool checkValidation = true + ) + { + // + ValidationProvider.NotEmpty(token); + + // + if (checkValidation) + { + ValidateToken(token); + } + + // + var result = token.ToMd5String().ToNormalString(); + + // + return result; + } + + /// + /// Generate Token string + /// + /// represent specified action token + /// token string + private string ToTokenString( + SecurityToken token + ) + { + // + ValidationProvider.NotNull(token, IdentityHelper); + + // + var result = IdentityHelper.ToTokenString(token); + + // + return result; + } + + /// + /// Generate Token string + /// + /// represent specified action token + /// token string + private string ToTokenString( + JwtSecurityToken token + ) + { + // + ValidationProvider.NotNull(token, IdentityHelper); + + // + var result = IdentityHelper.ToTokenString(token); + + // + return result; + } + + /// + /// Generate Security Token + /// + /// represent specified action token + /// an instance of SecurityToken + private SecurityToken ToSecurityToken( + string token + ) + { + // + ValidationProvider.NotEmpty(token); + + // + var result = IdentityHelper.ToSecurityToken(token); + + // + return result; + } + + /// + /// Generate Security Token + /// + /// an instance of XActionRequestToken + /// an instance of SecurityToken + private SecurityToken ToSecurityToken( + XActionRequestToken request + ) + { + // + ValidationProvider.NotNull(request); + + // + ValidateActionRequest(request); + + // + var result = IdentityHelper.ToSecurityToken(request); + if (result == null) + { + XException.InvalidToken.Throw(); + } + + // + return result; + } + + /// + /// Cleanup all exists tokens related to specified User + /// + /// specified user's identifier + /// + private async Task HandleRemoveExistsTokens( + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + var xTokens = new List(); + var xTokenEnumerator = DbContext.Tokens.AsAsyncEnumerable(); + await + foreach (var xToken in xTokenEnumerator) + { + // + if (xToken.UserSelectByParam == userSelectByParam && + ObjectHelper.ToEnumerableValues().Contains(xToken.Type)) + { + xTokens.Add(xToken); + } + } + + // + if (xTokens.HasChild()) + { + DbContext.Tokens.RemoveRange(xTokens); + await DbContext.SaveChangesAsync(); + } + } + + /// + /// Cleanup all tokens related to specified User + /// + /// a collection of user identifiers + /// + private async Task HandleCleanUserTokens( + ICollection userSelectByParams + ) + { + // + // Validate Args ... + if (!userSelectByParams.HasChild()) + { + return; + } + + // + foreach (var userSelectByParam in userSelectByParams) + { + await HandleRemoveExistsTokens(userSelectByParam); + } + } + + /// + /// Cleanup all tokens related to specified User + /// + /// an instance of XUser + /// + private async Task HandleCleanUserTokens( + XUser user + ) + { + // + if (user == null) + { + return; + } + + // + var userSelectByParams = GenerateUserSelectByParams(user); + + // + await HandleCleanUserTokens(userSelectByParams); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+User.cs + +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using IdentityModel; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using xCommons.Constants; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels; +using xIdentityModels.Constants; +using xIdentityModels.Dtos; +using xIdentityModels.Extensions; +using xIdentityModels.Navigations; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region User Handlers ... + /// + /// Check a User Exists or not + /// + /// specified user's identifier + /// a boolean value + public async Task IsUserExistsAsync( + string userSelectByParam + ) + { + // + // Validate Args ... + if (userSelectByParam.IsNullOrEmpty()) + { + return false; + } + + // + var selectType = GetUserSelectByType(userSelectByParam); + + // + var result = false; + XUser user = null; + switch (selectType) + { + case XUserSelectBy.MobileNumber: + user = await GetUsersDbSet() + .Where(ur => ur.PhoneNumber == userSelectByParam) + .FirstOrDefaultAsync(); + break; + + case XUserSelectBy.Email: + user = await UserManager + .FindByEmailAsync(userSelectByParam); + break; + + case XUserSelectBy.Username: + user = await UserManager + .FindByNameAsync(userSelectByParam); + break; + + case XUserSelectBy.ID: + default: + user = await UserManager + .FindByIdAsync(userSelectByParam); + break; + } + + // + result = user != null; + + // + return result; + } + + /// + /// Check a User Selector is Available for Registration or Not + /// + /// specified user's identifier + /// a boolean value + public async Task CanRegister( + string userSelectByParam + ) + { + // + // Validate ARgs ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + var selectByType = GetUserSelectByType(userSelectByParam); + + // + if (selectByType == XUserSelectBy.Username && + !ValidateUserName(userSelectByParam)) + { + return false; + } + + // + var isExists = await IsUserExistsAsync(userSelectByParam); + if (isExists) + { + return false; + } + + // + return true; + } + + /// + /// Get a User object + /// + /// specified user's identifier + /// specifies returned object contains all Navigation Properties or not, default is false + /// an instance of XUser + public async Task GetUserAsync( + string userSelectByParam, + bool containDetails = false + ) + { + // + var isExists = await IsUserExistsAsync(userSelectByParam); + if (!isExists) + { + XException.NotFound.Throw(); + } + + // + // Fill User ... + var selectType = GetUserSelectByType(userSelectByParam); + XUser user = null; + var usersStore = GetUsersDbSet(containDetails); + + // + // Select Type ... + switch (selectType) + { + case XUserSelectBy.MobileNumber: + user = await usersStore + .FirstOrDefaultAsync(ur => ur.PhoneNumber == + userSelectByParam); + break; + + case XUserSelectBy.Email: + user = await usersStore + .FirstOrDefaultAsync(ur => ur.Email == + userSelectByParam); + break; + + case XUserSelectBy.Username: + user = await usersStore + .FirstOrDefaultAsync(ur => ur.UserName == + userSelectByParam); + break; + + case XUserSelectBy.ID: + default: + user = await usersStore + .FirstOrDefaultAsync(ur => ur.Id == + userSelectByParam); + break; + } + + // + return user; + } + + /// + /// Retrieve User Names based on UserIds + /// + /// a collection of user identifiers + /// a collection of Usernames + public async Task> GetUserNamesAsync( + IEnumerable userIds + ) + { + // + // Validate Args ... + if (userIds == null || + userIds.Count() == 0) + { + return null; + } + + // + var result = await GetUsersDbSet() + .Where( + xu => + userIds + .Contains(xu.Id)) + .Select(xt => xt.UserName) + .ToListAsync(); + + // + return result; + } + + /// + /// get user ids and retrieve corresponding user names + /// + /// an instance of XUserNameIdRequest which represent required UserIds collection + /// a collection of XUserNameIdResponse instances + public async Task> GetUserNameIdsAsync( + XUserNameIdRequest model + ) + { + // + // Validate Args ... + if (model.IsNull() || !model.Ids.HasChild()) + { + return null; + } + + // + var result = await GetUsersDbSet() + .Where( + xu => + model.Ids.Contains(xu.Id) || + model.Ids.Contains(xu.Email) || + model.Ids.Contains(xu.UserName) || + model.Ids.Contains(xu.PhoneNumber)) + .Select(xt => new XUserNameIdResponse + { + Id = xt.Id, + UserName = xt.UserName, + }) + .ToListAsync(); + + // + return result; + } + + /// + /// Create a User + /// + /// an instance of XUser + /// user's password + /// check a user can log in in system or not, default is false + /// check user is banned or not, default is false + /// an instance of IdentityResult + public async Task CreateUserAsync( + XUser user, + string password, + bool checkCanLoginPolicies = false, + bool checkIsBanned = false + ) + { + // + if (user == null || + password.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + // Get User Select By param + var userSelectByParam = GetUserSelectByParam(user, excludes: new List { XUserSelectBy.ID }); + + // + // Check User Created before or not ... + var isExists = await IsUserExistsAsync(userSelectByParam); + if (isExists) + { + XException.Duplicate.Throw(); + } + + // + var result = await UserManager.CreateAsync(user, password); + if (result.Succeeded) + { + // + var claims = await ToJwtClaims( + user, + checkCanLoginPolicies: checkCanLoginPolicies, + checkIsBanned: checkIsBanned); + + // + var addClaimsResult = await UserManager.AddClaimsAsync(user, claims); + + // + return addClaimsResult; + } + + // + return result; + } + + /// + /// Update a User Information + /// + /// an instance of XUser + /// check a user can log in in system or not, default is true + /// check user is banned or not, default is true + /// an instance of IdentityResult + public async Task UpdateUserAsync( + XUser user, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ) + { + // + var updateResult = await this.UserManager.UpdateAsync(user); + if (updateResult.Succeeded) + { + // + var claims = await ToJwtClaims( + user, + checkCanLoginPolicies: checkCanLoginPolicies, + checkIsBanned: checkIsBanned + ); + var existsClaims = await UserManager.GetClaimsAsync(user); + + // + await UserManager.RemoveClaimsAsync(user, existsClaims); + var addClaimsResult = await UserManager.AddClaimsAsync(user, claims); + + // + return addClaimsResult; + } + + // + return updateResult; + } + + /// + /// Generate JWT Claims froma DashboardUser object + /// + /// an instance of XUser + /// check a user can log in in system or not, default is true + /// check user is banned or not, default is true + /// a collection of Claim instances + public async Task ToJwtClaims( + XUser user, + bool checkCanLoginPolicies = true, + bool checkIsBanned = true + ) + { + // + // Get New Instance of Claims ... + var claims = new List { + // + // Mapping to User.Identity.Name ... + new Claim (JwtClaimTypes.Name, user.UserName), + + // + // Mapping to User.Identity.Name ... + new Claim (JwtRegisteredClaimNames.UniqueName, user.UserName), + + // + // Other Usefull User Related Data ... + new Claim (JwtClaimTypes.GivenName, user.FirstName), + new Claim (JwtClaimTypes.FamilyName, user.LastName), + + // + // User Gendre ... + new Claim (JwtClaimTypes.Gender, user.Gender.ToString (), ClaimValueTypes.Integer), + + // + // User Profile Picture ... + new Claim (JwtClaimTypes.Picture, !user.Avatar.IsNullOrEmpty () ? + user.Avatar.ToString () : + ""), + + // + // The Unique Identifier for Each Token ... + new Claim (JwtRegisteredClaimNames.Jti, Guid.NewGuid ().ToString ()), + + // + // Add isEnable and isBanned feature ... + new Claim ( + XCustomClaims.IsEnabled, + user.IsEnable.ToString (), + ClaimValueTypes.Boolean + ), + new Claim ( + XCustomClaims.IsBanned, + user.IsBanned.ToString (), + ClaimValueTypes.Boolean + ), + }; + + // + #region Set Claims based on Profile Policies ... + // + if (Configuration.Policy.Profile.ContainsDateOfBirth) + { + claims.Add(new Claim(JwtRegisteredClaimNames.Birthdate, user.DateOfBirth.ToString())); + } + + // + if (Configuration.Policy.Profile.ContainsEmail) + { + claims.Add(new Claim(JwtClaimTypes.Email, user.Email)); + claims.Add(new Claim( + JwtClaimTypes.EmailVerified, + user.EmailConfirmed.ToString(), + ClaimValueTypes.Boolean)); + } + + // + if (Configuration.Policy.Profile.ContainsPhoneNumber) + { + claims.Add(new Claim(JwtClaimTypes.PhoneNumber, user.PhoneNumber)); + claims.Add(new Claim( + JwtClaimTypes.PhoneNumberVerified, + user.PhoneNumberConfirmed.ToString(), + ClaimValueTypes.Boolean)); + } + + // + if (Configuration.Policy.Profile.ContainsRoles) + { + var roleNames = await GetRoleNamesAsync( + user.UserName, + checkCanLoginPolicies: checkCanLoginPolicies, + checkIsBanned: checkIsBanned); + roleNames.ToList() + .ForEach(rn => + { + claims.Add(new Claim(JwtClaimTypes.Role, rn)); + }); + } + + // + if (Configuration.Policy.Profile.ContainsLastLogin) + { + claims.Add(new Claim( + JwtClaimTypes.AuthenticationTime, + user.LastLogin.ToString(), + ClaimValueTypes.DateTime)); + } + #endregion + + // + // Return Result ... + return claims.ToArray(); + } + + /// + /// Check User and Password relation + /// + /// an instance of XUser + /// user's password + /// an instance of XDevice + /// specify destination language + /// specify account lockout on failure log in + /// specify log in force without checking XDevice and lang relation + /// an instance of SignInResult + public async Task CheckPasswordSignInAsync( + XUser user, + string password, + XDevice device, + string language, + bool lockoutOnFailure, + bool isForced = true + ) + { + // + var result = await SignInManager.CheckPasswordSignInAsync( + user, password, lockoutOnFailure + ); + + // + if (result.Succeeded && isForced) + { + // + var isExistsDevice = user.Devices.Any(d => d.IsSameAs(device)); + if (!isExistsDevice && !user.Email.IsNullOrEmpty() && user.EmailConfirmed) + { + // + user.Devices.Add(device); + + // + var updateUserResult = await UpdateUserAsync(user); + if (updateUserResult.Succeeded) + { + // + try + { + // + var xMessage = GetUserNewDeviceLoggedInMessage( + language, + user.Email, + device + ); + + // + await MessageProvider.SendMailAsync(xMessage); + } + catch { } + } + } + } + + // + return result; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+Validators.cs + +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels; +using xIdentityModels.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIdentityModels.Navigations; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region User Validators ... + /// + /// Validate User Name for Registration based on Policies + /// + /// provided user name + /// a boolean value + private bool ValidateUserName( + string userName + ) + { + // + // Check min/max Length ... + if (userName.IsNullOrEmpty() || + userName.Length < Configuration.Policy.User.MinLength || + userName.Length > Configuration.Policy.User.MaxLength) + { + return false; + } + + // + // Check Allowed UserName Characters ... + if (!UserManager.Options.User.AllowedUserNameCharacters.IsNullOrEmpty() && + userName.Any(c => !UserManager.Options.User.AllowedUserNameCharacters.Contains(c))) + { + return false; + } + + // + // Check invalid UserNames ... + if (Configuration.Policy.User.InvalidUserNames + .HasChild() && Configuration.Policy.User.InvalidUserNames + .Any(u => userName + .ToNormalString() + .Contains(u.ToNormalString()))) + { + return false; + } + + // + return true; + } + + /// + /// Validate a User Exists + /// + /// specifies user identifier + /// specify Excetion to thrown on failure + /// + private async Task ValidateUserExistsAsync( + string userSelectByParam, + Exception exception = null + ) + { + // + ValidationProvider + .NotEmpty(userSelectByParam); + + // + if (exception == null) + { + exception = XException.NotFound.ToException(); + } + + // + // Check User Exists ... + var isExists = await IsUserExistsAsync(userSelectByParam); + if (!isExists) + { + throw exception; + } + } + + /// + /// Validate a User Not Exists + /// + /// specifies user identifier + /// specify Excetion to thrown on failure + /// + private async Task ValidateUserNotExists( + string userSelectByParam, + Exception exception = null + ) + { + // + ValidationProvider + .NotEmpty(userSelectByParam); + + // + if (exception == null) + { + exception = XException.UserRegisteredBefore.ToException(); + } + + // + // Check User Exists ... + var isExists = await IsUserExistsAsync(userSelectByParam); + if (isExists) + { + throw exception; + } + } + + /// + /// Validate User Exists and Some Usefull Checks + /// + /// specifies user identifier + /// specifies returned object contains all Navigation Properties or not, default is false + /// check a user can log in in system or not, default is false + /// check user is banned or not, default is true + /// specify do not thrown Exception if user is Disabled, default is false + /// specify requested person is Admin role, default is false + /// specify Excetion to thrown on failure + /// an instance of XUser + public async Task ValidateUserExistsAndRetrieve( + string userSelectByParam, + bool containDetails = false, + bool checkCanLoginPolicies = false, + bool checkIsBanned = true, + bool ignoreDisabledUser = false, + bool forceAdmin = false, + Exception exception = null + ) + { + // + ValidationProvider + .NotEmpty(userSelectByParam); + ValidationProvider.NotNull( + Configuration, + Configuration.Policy, + Configuration.Policy.SignIn); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidData.ToException(); + } + + // + // Check User Exists ... + await ValidateUserExistsAsync(userSelectByParam); + + // + // Retrieve User ... + var result = await GetUserAsync(userSelectByParam, containDetails); + if (result == null) + { + throw exception; + } + + // + // Check Force Admin ... + if (forceAdmin) + { + // + var isAdmin = await IsAdmin(result); + if (!isAdmin) + { + XException.NotAuthorized.Throw(); + } + } + + // + // Check User not Bann ... + if (checkIsBanned && + result.IsBanned) + { + XException.UserBanned.Throw(); + } + + // + var requireConfirmedEmail = Configuration.Policy.SignIn.RequireConfirmedEmail; + var requireConfirmedPhoneNumber = Configuration.Policy.SignIn.RequireConfirmedPhoneNumber; + var requireEnabled = Configuration.Policy.SignIn.RequiredEnabled; + + // + var isEnabled = result.IsEnable; + var isMobileConfirmed = result.PhoneNumberConfirmed; + var isEmailConfirmed = result.EmailConfirmed; + + // + if (checkCanLoginPolicies) + { + // + // Check IsEnabled ... + if (!ignoreDisabledUser && + requireEnabled && + !isEnabled) + { + XException.UserDisabled.Throw(); + } + + // + // Check Email Confirmed ... + if (requireConfirmedEmail && + !isEmailConfirmed) + { + XException.EmailNotConfirmed.Throw(); + } + + // + // Check Mobile Confirmed ... + if (requireConfirmedPhoneNumber && + !isMobileConfirmed) + { + XException.MobileNotConfirmed.Throw(); + } + } + + // + // Return Result ... + return result; + } + + /// + /// Validate a Device for User Actions + /// + /// an instance of XDevice + /// + private async Task ValidateDeviceForActions( + XDevice device + ) + { + // + ValidationProvider + .NotNull(device); + + // + // Check is Device Banned or not ... + var isDeviceBanned = await IsDeviceBanned(device); + if (isDeviceBanned) + { + // + // Retrieve Banned Device ... + var xBannedDevice = await GetBannedDevice(device); + if (xBannedDevice == null) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Banned Device Time out Passed or not ... + var isDelayTimePassed = IsDelayTimePassed(xBannedDevice); + if (!isDelayTimePassed) + { + // + var passedTime = GetPassedTime(xBannedDevice); + + // + XException.DeviceBanned + .AddContentToException(passedTime.ToString()); + } + + // + // if Delay Passed of Banned Device + // the device must Removed ... + await BannedDeviceRemove(xBannedDevice); + } + } + + /// + /// Validate a DateOfBirth for Registration + /// + /// users dob date, an instance of DateTime + /// specify Excetion to thrown on failure + /// + private void ValidateDateOfBirth( + DateTime dateOfBirth, + Exception exception = null + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(dateOfBirth); + + // + if (exception.IsNull()) + { + exception = XException.InvalidDate.ToException(); + } + + // + var currentDate = DateTime.UtcNow; + if (dateOfBirth >= currentDate) + { + throw exception; + } + + // + var zeroDate = (new DateTime(1, 1, 1)).ToUniversalTime(); + var minAge = Configuration.Policy.User.MinAgeForRegistration; + var maxAge = Configuration.Policy.User.MaxAgeForRegistration; + + // + var timeSpan = currentDate - dateOfBirth; + var age = (zeroDate + timeSpan).Year - 1; + + // + if (age > maxAge || age < minAge) + { + throw exception; + } + } + + /// + /// Validate a Given User Can Registere + /// + /// specifies user identifier + /// + private async Task ValidateCanRegister( + string userSelectByParam + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(userSelectByParam); + + // + var canRegister = await CanRegister(userSelectByParam); + if (!canRegister) + { + XException.Unavailable.Throw(); + } + } + + /// + /// Validate Password by Gicen Policies + /// + /// an instance of XUser + /// user's password + /// specify Excetion to thrown on failure + /// + private async Task ValidatePasswordPolicies( + XUser user, + string password, + Exception exception = null + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(user); + ValidationProvider.NotEmpty(password); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidPassword.ToException(); + } + + // + // Check new Password Validation ... + var passwordValidator = new PasswordValidator(); + var checkPasswordResult = await passwordValidator.ValidateAsync(UserManager, user, password); + if (!checkPasswordResult.Succeeded) + { + throw exception; + } + } + + /// + /// Validate a User for Dangerous Actions + /// + /// an instance of XUser + /// specified which user request Action by an instance of XUser + /// + private async Task ValidateUserForDangerousAction( + XUser user, + XUser requester + ) + { + // + // Validate ARgs ... + ValidationProvider.NotNull(user, requester); + + // + var requesterRoleNames = await GetRoleNamesAsync(requester.Id); + + // + var isSame = user.Id == requester.Id; + var isRequesterAdmin = requesterRoleNames.Any(r => r.ToNormalString() == "admin"); + if (!isSame && !isRequesterAdmin) + { + XException.NotAllowed.Throw(); + } + } + #endregion + + // + #region Device Validators ... + /// + /// Validate a Device for Specified User + /// + /// specifies user identifier + /// an instance of XDevice + /// specify Excetion to thrown on failure + /// + private async Task ValidateUserAndDeviceRelation( + string userSelectByParam, + XDevice device, + Exception exception = null + ) + { + // + ValidationProvider.NotEmpty(userSelectByParam); + ValidationProvider.NotNull(device); + + // + if (exception == null) + { + exception = XException.InvalidDevice.ToException(); + } + + // + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: true); + + // + var result = await IsDeviceRelateDToUser( + userSelectByParam, + device + ); + if (!result) + { + throw exception; + } + } + #endregion + + // + #region Token Validators ... + /// + /// Validate a Token + /// + /// token string + /// specify Excetion to thrown on failure + private void ValidateToken( + string token, + Exception exception = null + ) + { + // + ValidationProvider.NotEmpty(token); + + // + if (exception == null) + { + exception = XException.InvalidToken.ToException(); + } + + // + var result = IdentityHelper.ValidateToken(token); + if (!result) + { + throw exception; + } + } + + /// + /// Validate a Token is Specified to a Device and User + /// + /// an instance of XDevice + /// specifies Action type by a member of XAction + /// specify Excetion to thrown on failure + /// an instance of XToken + private async Task ValidateAndRetieveTokenByDeviceAndType( + XDevice device, + XAction type, + Exception exception = null + ) + { + // + ValidationProvider.NotNull(device); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.NotFound.ToException(); + } + + // + var isExists = await IsTokenExistsByDeviceAndType(device, type); + if (!isExists) + { + throw exception; + } + + // + var item = await GetTokenByDeviceAndType(device, type); + if (item == null) + { + throw exception; + } + + // + return item; + } + + /// + /// Validate a Token By User and Specified Type + /// + /// specifies user identifier + /// specifies Action type by a member of XAction + /// specify Excetion to thrown on failure + /// an instance of XToken + private async Task ValidateAndRetieveTokenByUserAndType( + string userSelectByParam, + XAction type, + Exception exception = null + ) + { + // + ValidationProvider.NotEmpty(userSelectByParam); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidData.ToException(); + } + + // + var isExists = await IsTokenExistsByUserAndType(userSelectByParam, type); + if (!isExists) + { + throw exception; + } + + // + var item = await GetTokenByUserAndType(userSelectByParam, type); + if (item == null) + { + throw exception; + } + + // + return item; + } + + /// + /// Check Token Validation + /// + /// token string + /// a boolean value + private bool IsValidToken( + string token + ) + { + // + ValidationProvider.NotEmpty(token); + + // + var result = IdentityHelper.ValidateToken(token); + + // + return result; + } + + /// + /// Retrieve Action Request Based on Action Token + /// + /// token string + /// specify Excetion to thrown on failure + /// an instance of XActionRequestToken + private XActionRequestToken ValidateAndParseActionToken( + string token, + Exception exception = null + ) + { + // + ValidationProvider.NotEmpty(token); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidToken.ToException(); + } + + // + ValidateToken(token); + + // + var result = IdentityHelper.ParseActionRequestToken(token); + if (result == null) + { + throw exception; + } + + // + ValidateActionRequest(result); + + // + return result; + } + + /// + /// Parse Action Request from Token + /// + /// token string + /// an instance of XActionRequestToken + private XActionRequestToken ParseActionRequest( + string token + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(token); + + // + var result = IdentityHelper + .ParseActionRequestToken(token); + + // + return result; + } + + /// + /// Parse Action Hash by Corresponding Token + /// + /// token hash string + /// an instance of XActionRequestToken + private async Task ParseActionHash( + string hash + ) + { + // + ValidationProvider.NotEmpty(hash); + + // + var isHashExists = await IsTokenExistsByHash(hash); + if (!isHashExists) + { + return null; + } + + // + var xToken = await GetTokenByHash(hash); + if (xToken == null) + { + return null; + } + + // + var isValidToken = IsValidToken(xToken.Token); + if (!isValidToken) + { + return null; + } + + // + var result = ParseActionRequest(xToken.Token); + + // + return result; + } + + /// + /// Validate and Parse Action Hash + /// + /// token hash string + /// specifies chack token Validation Parameter, default is true + /// specifies check action Validations, default is true + /// specify Excetion to thrown on failure + /// an instance of XActionRequestToken + private async Task ValidateAndParseActionHash( + string hash, + bool forceTokenValidation = true, + bool forceActionValidation = true, + Exception exception = null + ) + { + // + ValidationProvider.NotEmpty(hash); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidToken.ToException(); + } + + // + var token = await ValidateAndRetrieveRelatedToken(hash, forceTokenValidation); + + // + var result = await ParseActionHash(hash); + if (result == null) + { + throw exception; + } + + // + if (forceActionValidation) + { + ValidateActionRequest(result); + } + + // + return result; + } + + /// + /// Validate Action Request + /// + /// an instance of XActionRequestToken + /// specify Excetion to thrown on failure + private void ValidateActionRequest( + XActionRequestToken request, + Exception exception = null + ) + { + // + ValidationProvider + .NotNull(request); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidData.ToException(); + } + + // + var isValid = IsValidActionRequest(request); + if (!isValid) + { + throw exception; + } + } + + /// + /// Check Action Request Validation + /// + /// an instance of XActionRequestToken + /// a boolean value + private bool IsValidActionRequest( + XActionRequestToken request + ) + { + // + // Validate Args ... + if (request == null || + Configuration.IdentitySecretKey.IsNullOrEmpty()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = request.Validate(Configuration.IdentitySecretKey); + + // + return result; + } + + /// + /// Check Token and Act Based on it + /// + /// token string + /// specify Excetion to thrown on Issue failure + /// specify Excetion to thrown on failure + /// + private async Task ValidateAndHandleToken( + string token, + bool issueExcepion = false, + Exception exception = null + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(token); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidToken.ToException(); + } + + // + var isValidToken = IsValidToken(token); + + // + if (!isValidToken) + { + // + await RemoveTokenByToken(token); + + // + if (issueExcepion) + { + throw exception; + } + } + } + + /// + /// Validate a Hash Exists or not + /// + /// token hash string + /// specify Excetion to thrown on failure + /// + private async Task ValidateHashExists( + string hash, + Exception exception = null + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(hash); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidToken.ToException(); + } + + // + var isExists = await IsTokenExistsByHash(hash); + if (!isExists) + { + throw exception; + } + } + + /// + /// Validate a Token Exists + /// + /// token string + /// specify Excetion to thrown on failure + /// + private async Task ValidateTokenExists( + string token, + Exception exception = null + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(token); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidToken.ToException(); + } + + // + var isExists = await IsTokenExistsByToken(token); + if (!isExists) + { + throw exception; + } + } + + /// + /// Validate a Hash and Retrieve it's Corresponding Token + /// + /// token hash string + /// specifies chack token Validation Parameter, default is true + /// specify Excetion to thrown on Issue failure + /// specify Excetion to thrown on failure + /// token string + private async Task ValidateAndRetrieveRelatedToken( + string hash, + bool forceTokenValidation = true, + bool issueExcepion = false, + Exception exception = null + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(hash); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidToken.ToException(); + } + + // + await ValidateHashExists(hash); + + // + var token = await GetRelatedTokenByHash(hash); + if (token.IsNullOrEmpty()) + { + throw exception; + } + + // + if (forceTokenValidation) + { + await ValidateAndHandleToken(token, issueExcepion); + } + + // + return token; + } + + /// + /// Validate a Token and Retrieve it's Corresponding Hash + /// + /// token string + /// specifies chack token Validation Parameter, default is true + /// specify Excetion to thrown on failure + /// hash string + private async Task ValidateAndRetrieveRelatedHash( + string token, + bool forceTokenValidation = true, + Exception exception = null + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty(token); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidToken.ToException(); + } + + // + if (forceTokenValidation) + { + await ValidateAndHandleToken(token); + } + + // + await ValidateTokenExists(token); + + // + var hash = await GetRelatedHashByToken(token); + if (hash.IsNullOrEmpty()) + { + throw exception; + } + + // + return hash; + } + + /// + /// Validate Invitation Hash + /// + /// registration invitation hash string + /// thrown Exception if user Exists, default is true + /// + private async Task ValidateInvitationHash( + string invitationHash, + bool forceUserExists = true + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(Configuration); + + // + if (Configuration.RegistrationJustWithInvite && + invitationHash.IsNullOrEmpty()) + { + XException.NotAllowed.Throw(); + } + + // + // Validate Invitation Token if it isn't null ... + if (!invitationHash.IsNullOrEmpty()) + { + // + await ValidateHashExists(invitationHash); + + // + // Token Validation also done in this task ... + var relatedToken = await ValidateAndRetrieveRelatedToken(invitationHash); + + // + var inviteAction = ValidateAndParseActionToken(relatedToken); + ValidationProvider.NotNull(inviteAction); + + // + var inviteUerSelectByParam = GetUserSelectByParam(inviteAction); + var inviteUserSelectBy = GetUserSelectByType(inviteUerSelectByParam); + if (inviteUserSelectBy != XUserSelectBy.Email) + { + XException.InvalidToken.Throw(); + } + + // + if (forceUserExists) + { + // + await ValidateUserNotExists( + inviteUerSelectByParam, + XException.EmailInUsed.ToException()); + } + } + } + #endregion + + // + #region Profile Validators ... + /// + /// Validate a Request is Corresponding to Specific Device + /// + /// an instance of XDevice + /// specify requested device by an instance of XDevice + /// specify Excetion to thrown on failure + private void ValidateRequestAndGiveDevices( + XDevice device, + XDevice requestDevice, + Exception exception = null + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(device, requestDevice); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidDevice.ToException(); + } + + // + var isDevicesSame = device.IsSameAs(requestDevice); + + // + if (!isDevicesSame) + { + throw exception; + } + } + + /// + /// Check Validation of a User SelectBy Param and Password + /// + /// specifies user identifier + /// user's password + /// check a user can log in in system or not, default is true + /// specify Excetion to thrown on failure + /// + private async Task ValidateUserAndPassword( + string userSelectByParam, + string password, + bool checkCanLoginPolicies = true, + Exception exception = null + ) + { + // + // Validate Args ... + ValidationProvider.NotEmpty( + userSelectByParam, + password + ); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.LoginFailed.ToException(); + } + + // + // Retrieve User and Validate User Exists ... + var user = await ValidateUserExistsAndRetrieve( + userSelectByParam, + containDetails: false, + checkCanLoginPolicies: checkCanLoginPolicies, + ignoreDisabledUser: false, + checkIsBanned: true, + exception: exception); + + // + // Check Given Password Follows Policies ... + await ValidatePasswordPolicies(user, password, exception); + + // + // Check Password for User ... + var checkPasswordResult = await UserManager.CheckPasswordAsync(user, password); + if (!checkPasswordResult) + { + throw exception; + } + } + #endregion + + // + #region Friendship Validators ... + /// + /// Validate Friendship Requested Before or not + /// + /// specifies a user to check as source by an instance of XUser + /// specifies a user to check as dest by an instance of XUser + private void ValidateRequestBefore( + XUser source, + XUser dest + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(source, dest); + + // + var isRequested = IsFollowRequested(source, dest); + if (!isRequested) + { + XException.NotFound.Throw(); + } + } + + /// + /// Validate a Friendship Not Requested Before + /// + /// specifies a user to check as source by an instance of XUser + /// specifies a user to check as dest by an instance of XUser + private void ValidateNotRequestBefore( + XUser source, + XUser dest + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(source, dest); + + // + var isRequested = IsFollowRequested(source, dest); + if (isRequested) + { + XException.Duplicate.Throw(); + } + } + + /// + /// Validate User Can Send Following Request to Dest User + /// + /// specifies a user to check as dest by an instance of XUser + /// specifies a user to check as source by an instance of XUser + private void ValidateFollowingRequest( + XUser accepterUser, + XUser requesterUser + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(accepterUser, requesterUser); + + // + var isRequest = IsFollowRequested(requesterUser, accepterUser, XFriendshipState.Pending); + if (!isRequest) + { + XException.NotFound.Throw(); + } + } + + /// + /// Validate a User can Accept a Following Request or Not + /// + /// specifies a user to check as dest by an instance of XUser + /// specifies a user to check as source by an instance of XUser + private void ValidateFollowingRequestForAccept( + XUser accepterUser, + XUser requesterUser + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(accepterUser, requesterUser); + + // + var isRequestPending = IsFollowRequested(requesterUser, accepterUser, XFriendshipState.Pending); + var isRequestRejected = IsFollowRequested(requesterUser, accepterUser, XFriendshipState.Rejected); + if (!isRequestPending && + !isRequestRejected) + { + XException.NotFound.Throw(); + } + } + + /// + /// Validate a Request is not Passed for Same Users + /// + /// specifies a user to check as source by an instance of XUser + /// specifies a user to check as dest by an instance of XUser + private void ValidateNotSameUsers( + XUser source, + XUser dest + ) + { + // + // Validate ARgs ... + ValidationProvider.NotNull(source, dest); + + // + var isSame = source.Id == dest.Id; + if (isSame) + { + XException.ActionFailed.Throw(); + } + } + + /// + /// Check a user is in Followers of another + /// + /// specifies a user to check as source by an instance of XUser + /// specifies a user to check as dest by an instance of XUser + private void ValidateIsFollowers( + XUser source, + XUser dest + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(source, dest); + + // + ValidateNotSameUsers(source, dest); + ValidateRequestBefore(dest, source); + + // + var isFollower = IsFollowRequested(dest, source, XFriendshipState.Accepted) || + IsFollowRequested(dest, source, XFriendshipState.Blocked); + if (!isFollower) + { + XException.NotFound.Throw(); + } + } + + /// + /// Check a user is in Followings of another + /// + /// specifies a user to check as source by an instance of XUser + /// specifies a user to check as dest by an instance of XUser + private void ValidateIsFollowings( + XUser source, + XUser dest + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(source, dest); + + // + ValidateNotSameUsers(source, dest); + ValidateRequestBefore(source, dest); + + // + var isFollowing = IsFollowRequested(source, dest, XFriendshipState.Accepted); + if (!isFollowing) + { + XException.NotFound.Throw(); + } + } + + /// + /// Validate a User Blocked another + /// + /// specifies a user to check as source by an instance of XUser + /// specifies a user to check as dest by an instance of XUser + private void ValidateIsBlock( + XUser source, + XUser dest + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(source, dest); + + // + ValidateNotSameUsers(source, dest); + ValidateRequestBefore(dest, source); + + // + var isFollower = IsFollowRequested(dest, source, XFriendshipState.Blocked); + if (!isFollower) + { + XException.NotFound.Throw(); + } + } + #endregion + + // + #region XActionRequest Validators ... + /// + /// Validate a Device for Request + /// + /// an instance of XDevice + /// specify requested device by an instance of XDevice + /// specify Excetion to thrown on failure + private void ValidateRequestAndGivenDevices( + XDevice device, + XDevice requestDevice, + Exception exception = null + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(device, requestDevice); + + // + // Prepare Default Exception ... + if (exception == null) + { + exception = XException.InvalidDevice.ToException(); + } + + // + var isDevicesSame = device.IsSameAs(requestDevice); + + // + if (!isDevicesSame) + { + throw exception; + } + } + #endregion + + // + #region XVerification Request ... + /// + /// Validate Verification Request Exists by Device + /// + /// an instance of XDevice + /// specify Excetion to thrown on failure + /// + private async Task ValidateVerificationRequestExists( + XDevice device, + Exception exception = null + ) + { + // + ValidationProvider.NotNull(device); + + // + if (exception == null) + { + exception = XException.InvalidDevice.ToException(); + } + + // + var isExists = await IsDeviceRequestedForVerificationCodeBefor(device); + if (!isExists) + { + throw exception; + } + } + + /// + /// Validate Verification Request Exists by VerificationCode + /// + /// Confirm Verification Code + /// specify Excetion to thrown on failure + /// + private async Task ValidateVerificationRequestExists( + string verificationCode, + Exception exception = null + ) + { + // + ValidationProvider.NotEmpty(verificationCode); + + // + if (exception == null) + { + exception = XException.InvalidVerificationCode.ToException(); + } + + // + var isExists = await IsVerificationCodeRequested(verificationCode); + if (!isExists) + { + throw exception; + } + } + + /// + /// Validate and Retrive Verification Request for Given Device + /// + /// an instance of XDevice + /// specify Excetion to thrown on failure + /// an instance of XVerificationRequest + private async Task ValidateAndRetrieveVerificationRequest( + XDevice device, + Exception exception = null + ) + { + // + ValidationProvider.NotNull(device); + + // + if (exception == null) + { + exception = XException.InvalidDevice.ToException(); + } + + // + await ValidateVerificationRequestExists(device); + + // + var result = await GetVerificationRequest(device); + if (result == null) + { + throw exception; + } + + // + return result; + } + + /// + /// Validate and Retrieve Verification Request for Given VerificationCode + /// + /// Confirm Verification Code + /// specify Excetion to thrown on failure + /// an instance of XVerificationRequest + private async Task ValidateAndRetrieveVerificationRequest( + string verificationCode, + Exception exception = null + ) + { + // + ValidationProvider.NotEmpty(verificationCode); + + // + if (exception == null) + { + exception = XException.InvalidVerificationCode.ToException(); + } + + // + await ValidateVerificationRequestExists(verificationCode); + + // + var result = await GetVerificationRequest(verificationCode); + if (result == null) + { + throw exception; + } + + // + return result; + } + + /// + /// Handle Prepare Verification Request + /// + /// Confirm Verification Code + /// an instance of XActionRequestToken + /// an instance of XVerificationRequest + private async Task HandleVerificationRequestPreparation( + string verificationCode, + XActionRequestToken request + ) + { + // + ValidationProvider + .NotEmpty(verificationCode); + + // + // Check Action Request Validation ... + ValidateActionRequest(request); + + // + var device = request.Context.Device; + + // + // Check is Device Requested Before or not ... + XVerificationRequest xVerificationRequest = null; + var isDeviceRequested = await IsDeviceRequestedForVerificationCodeBefor(device); + if (!isDeviceRequested) + { + // + xVerificationRequest = new XVerificationRequest + { + VerificationCode = verificationCode, + NumberOfTries = 1, + LastTryOn = DateTime.UtcNow, + Device = device, + Request = request + }; + + // + await AddVerificationRequest(xVerificationRequest); + } + else + { + // + // Get Verification Request ... + xVerificationRequest = await ValidateAndRetrieveVerificationRequest(device); + } + + // + return xVerificationRequest; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager\XIdentityManager+VerificationCode.cs + +using System.Threading.Tasks; +using xCommons.Extensions; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xIdentityModels.Navigations; + +namespace xIds.Providers +{ + public partial class XIdentityManager + { + // + #region Verification Code Actions ... + /// + /// Check a Device Requeste for Verification Code before or not + /// + /// an instance of XDevice + /// a boolean value + private async Task IsDeviceRequestedForVerificationCodeBefor( + XDevice device + ) + { + // + var result = false; + var enumerator = DbContext.VerificationRequests + .AsAsyncEnumerable(); + await + foreach (var entity in enumerator) + { + // + if (entity.Device.IsSameAs(device)) + { + // + result = true; + break; + } + } + + // + return result; + } + + /// + /// Check a Verification Code Requested before or not + /// + /// Confirm Verification Code + /// a boolean value + private async Task IsVerificationCodeRequested( + string verificationCode + ) + { + // + // Validate Args ... + if (verificationCode.IsNullOrEmpty()) + { + return false; + } + + // + var result = false; + var enumerator = DbContext.VerificationRequests + .AsAsyncEnumerable(); + await + foreach (var entity in enumerator) + { + // + if (entity.VerificationCode == verificationCode) + { + // + result = true; + break; + } + } + + // + return result; + } + + /// + /// Retrieve Verification Request + /// + /// an instance of XDevice + /// an instance of XVerificationRequest + private async Task GetVerificationRequest( + XDevice device + ) + { + // + XVerificationRequest result = null; + var enumerator = DbContext.VerificationRequests + .AsAsyncEnumerable(); + await + foreach (var entity in enumerator) + { + // + if (entity.Device.IsSameAs(device)) + { + // + result = entity; + break; + } + } + + // + return result; + } + + /// + /// Retrieve Verification Request + /// + /// Confirm Verification Code + /// an instance of XVerificationRequest + private async Task GetVerificationRequest( + string verificationCode + ) + { + // + XVerificationRequest result = null; + var enumerator = DbContext.VerificationRequests + .AsAsyncEnumerable(); + await + foreach (var entity in enumerator) + { + // + if (entity.VerificationCode == verificationCode) + { + // + result = entity; + break; + } + } + + // + return result; + } + + /// + /// Remove Exists Verification Code Request + /// + /// an instance of XDevice + /// save changes on DbContext, default is true + /// + private async Task RemoveVerificationCodeRequestByDevice( + XDevice device, + bool saveChanges = true + ) + { + // + if (!await IsDeviceRequestedForVerificationCodeBefor(device)) + { + return; + } + + // + var xvr = await GetVerificationRequest(device); + DbContext.VerificationRequests.Remove(xvr); + + // + if (saveChanges) + { + await DbContext.SaveChangesAsync(); + } + } + + /// + /// Remove Exists Verification Code Request + /// + /// Confirm Verification Code + /// save changes on DbContext, default is true + /// + private async Task RemoveVerificationRequest( + string verificationCode, + bool saveChanges = true + ) + { + // + ValidationProvider.NotEmpty(verificationCode); + + // + var request = await GetVerificationRequest(verificationCode); + DbContext.VerificationRequests.Remove(request); + + // + if (saveChanges) + { + await DbContext.SaveChangesAsync(); + } + } + + /// + /// Add Verification Code Request + /// + /// an instance of XVerificationRequest + /// save changes on DbContext, default is true + /// an instance of XVerificationRequest + private async Task AddVerificationRequest( + XVerificationRequest request, + bool saveChanges = true + ) + { + // + if (await IsDeviceRequestedForVerificationCodeBefor(request.Device)) + { + await RemoveVerificationCodeRequestByDevice(request.Device); + } + + // + var entry = await DbContext.VerificationRequests.AddAsync(request); + + // + if (saveChanges) + { + await DbContext.SaveChangesAsync(); + } + + // + return entry.Entity; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityManager.cs + +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using xCommons.Extensions; +using xCommons.Providers; +using xExceptions.Constants; +using xIdentityModels; +using xIdentityModels.Configurations; +using xIdentityModels.Constants; +using xIdentityModels.Models; +using xMessageService.Interfaces; +using xStorageService.Interfaces; +using xIds.Configurations; +using xIds.DbContext; +using xIds.Interfaces; + +namespace xIds.Providers +{ + public partial class XIdentityManager : IXIdentityManager + { + // + private JsonSerializerSettings jsonSerializerSettings; + + // + public XIdentityDbContext DbContext { get; } + public UserManager UserManager { get; } + public IXIdentityHelper IdentityHelper { get; } + public ILogger Logger { get; } + public IXMessageProvider MessageProvider { get; } + public IXStorageProvider StorageProvider { get; } + public SignInManager SignInManager { get; } + public IXSecurityProvider SecurityProvider { get; } + public XIdentityConfiguration Configuration { get; } + public RoleManager RoleManager { get; } + public XValidationProvider ValidationProvider { get; } + public XDataServiceConfiguration DataConfiguration { get; } + public IXIdentityMessageProvider IdentityMessageProvider { get; } + public XIdentityResourceConfiguration IdentityResourceConfiguration { get; } + + public XIdentityManager( + XIdentityDbContext dbContext, + UserManager userManager, + IXIdentityHelper identityHelper, + ILogger logger, + IXMessageProvider messageProvider, + IXStorageProvider storageProvider, + SignInManager signInManager, + IXSecurityProvider securityProvider, + XIdentityConfiguration configuration, + RoleManager roleManager, + XValidationProvider validationProvider, + XDataServiceConfiguration dataConfiguration, + IXIdentityMessageProvider identityMessageProvider, + XIdentityResourceConfiguration identityResourceConfiguration + ) + { + // + Logger = logger; + DbContext = dbContext; + RoleManager = roleManager; + UserManager = userManager; + SignInManager = signInManager; + SecurityProvider = securityProvider; + Configuration = configuration; + IdentityHelper = identityHelper; + MessageProvider = messageProvider; + StorageProvider = storageProvider; + DataConfiguration = dataConfiguration; + ValidationProvider = validationProvider; + IdentityMessageProvider = identityMessageProvider; + IdentityResourceConfiguration = identityResourceConfiguration; + + // + this.jsonSerializerSettings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + } + + // + #region Class Getter (s) Methods ... + /// + /// Retrieve User DB Set + /// + /// specifies returned object contains all Navigation Properties or not, default is false + /// + public IQueryable GetUsersDbSet( + bool containDetails = false + ) + { + // + var dbSet = containDetails ? GetUsersFullDbSet() : + UserManager.Users; + return dbSet; + } + + /// + /// Retrieve User Db Set Full Navigation Properties + /// + /// + /// + public IQueryable GetUsersFullDbSet(bool enableTracking = false) + { + // + var dbSet = UserManager.Users + .Include(u => u.Roles) + .Include(u => u.Devices) + .Include(u => u.Followers) + .Include(u => u.Followings) + .Include(u => u.Avatars); + + // + return dbSet.AsSplitQuery(); + } + #endregion + + // + #region Tools ... + /// + /// Get User SelectBy Param + /// + /// + /// + /// + /// + public string GetUserSelectByParam( + XActionRequest model, + bool forceNotNull = true, + ICollection excludes = null + ) + { + // + // Validate Args ... + ValidationProvider.NotNull(model); + + // + var id = model.UserId; + var userName = model.UserName; + var email = model.Email; + var phoneNumber = model.MobileNumber; + + // + if (excludes != null) + { + // + if (excludes.Contains(XUserSelectBy.ID)) + { + id = null; + } + if (excludes.Contains(XUserSelectBy.Email)) + { + email = null; + } + if (excludes.Contains(XUserSelectBy.MobileNumber)) + { + phoneNumber = null; + } + if (excludes.Contains(XUserSelectBy.Username)) + { + userName = null; + } + } + + var selectByParam = + id.IsNullOrEmpty() ? + userName.IsNullOrEmpty() ? + email.IsNullOrEmpty() ? + phoneNumber.IsNullOrEmpty() ? null : phoneNumber : email : userName : id; + + // + // Check result ... + if (forceNotNull && + selectByParam.IsNullOrEmpty()) + { + XException.InvalidData.Throw(); + } + + // + return selectByParam; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityMessageProvider.cs + +using System.Linq; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Configurations; +using xIds.Constants; +using xIds.Interfaces; + +namespace xIds.Providers +{ + public class XIdentityMessageProvider : IXIdentityMessageProvider + { + // + public string InviteMsg { get; set; } + public string RegistrationApproveMsg { get; set; } + public string RegisteredMsg { get; set; } + public string VerificationCodeMsg { get; set; } + public string ChangePasswordMsg { get; set; } + public string PasswordChangedMsg { get; set; } + public string NewDeviceLoggedInMsg { get; set; } + + private readonly ILogger logger; + private readonly XIdentityConfiguration identityConfiguration; + + public XIdentityMessageProvider( + ILogger logger, + XIdentityConfiguration identityConfiguration + ) + { + this.logger = logger; + this.identityConfiguration = identityConfiguration; + } + + /// + /// Determine All things is Ready or Not + /// + /// + public bool IsReady() + { + return !InviteMsg.IsNullOrEmpty() && + !RegistrationApproveMsg.IsNullOrEmpty() && + !RegisteredMsg.IsNullOrEmpty() && + !VerificationCodeMsg.IsNullOrEmpty() && + !ChangePasswordMsg.IsNullOrEmpty() && + !PasswordChangedMsg.IsNullOrEmpty() && + !NewDeviceLoggedInMsg.IsNullOrEmpty(); + } + + /// + /// Retrieve a Translated Value Based on Given Lang + /// + /// + /// + /// + private string GetStringResource(string resourceTitle, string lang) + { + // + var item = identityConfiguration.IdentityMessages + .FirstOrDefault(sr => + sr.Language.ToNormalString() == lang.ToNormalString() && + sr.ResourceTitle.ToNormalString() == resourceTitle.ToNormalString()); + // + if (item == null) + { + return null; + } + + // + return item.TranslatedValue; + } + + /// + /// Prepare Helper Class and Fill Messages + /// with Specific Language + /// + /// + public void PrepareMessages(string lang) + { + // + InviteMsg = GetStringResource(IdentityMessagesKeys.InviteMsg, lang); + RegisteredMsg = GetStringResource(IdentityMessagesKeys.RegisteredMsg, lang); + ChangePasswordMsg = GetStringResource(IdentityMessagesKeys.ChangePasswordMsg, lang); + PasswordChangedMsg = GetStringResource(IdentityMessagesKeys.PasswordChangedMsg, lang); + VerificationCodeMsg = GetStringResource(IdentityMessagesKeys.VerificationCodeMsg, lang); + NewDeviceLoggedInMsg = GetStringResource(IdentityMessagesKeys.NewDeviceLoggedInMsg, lang); + RegistrationApproveMsg = GetStringResource(IdentityMessagesKeys.RegistrationApproveMsg, lang); + + // + if (!IsReady()) + { + XException.InvalidArgs.Throw(); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XIdentityProfileService.cs + +using System.Linq; +using System.Threading.Tasks; +using IdentityServer4.Extensions; +using IdentityServer4.Models; +using IdentityServer4.Services; +using Microsoft.AspNetCore.Identity; +using xCommons.Extensions; +using xIdentityModels; +using xIds.Interfaces; + +namespace xIds.Providers +{ + public class XIdentityProfileService : IProfileService + { + private readonly IXIdentityManager identityManager; + private readonly IUserClaimsPrincipalFactory userClaimsPrincipalFacotry; + + public XIdentityProfileService( + IXIdentityManager identityManager, + IUserClaimsPrincipalFactory userClaimsPrincipalFacotry + ) + { + this.identityManager = identityManager; + this.userClaimsPrincipalFacotry = userClaimsPrincipalFacotry; + } + + public async Task GetProfileDataAsync(ProfileDataRequestContext context) + { + // + var sub = context.Subject.GetSubjectId(); + var user = await identityManager.GetUserAsync(sub, true); + var principal = await userClaimsPrincipalFacotry.CreateAsync(user); + + // + var claims = principal.Claims.ToList(); + + // + context.IssuedClaims = claims; + } + + public async Task IsActiveAsync(IsActiveContext context) + { + // + var sub = context.Subject.GetSubjectId(); + var user = await identityManager.GetUserAsync(sub); + + // + context.IsActive = !user.IsNull() && user.IsEnable && !user.IsBanned; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Providers\XSecurityProvider.cs + +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using xCommons.Extensions; +using xCommons.Providers; +using xIds.Configurations; +using xIds.Interfaces; + +namespace xIds.Providers +{ + public class XSecurityProvider : IXSecurityProvider + { + // + #region Props ... + private readonly string secretKey; + private readonly byte[] iv; + private readonly byte[] key; + private readonly XValidationProvider validationProvider; + #endregion + + // + #region Constructor ... + public XSecurityProvider( + XIdentityResourceConfiguration identityConfiguration, + XValidationProvider validationProvider + ) + { + // + this.secretKey = identityConfiguration.XRevisionSecretKey.ToMd5String(); + this.validationProvider = validationProvider; + + // + var ivBytes = new byte[16]; + var keyBytes = new byte[32]; + var secretBytes = Encoding.UTF8.GetBytes(this.secretKey); + + // + Array.Copy( + secretBytes, + ivBytes, + secretBytes.Length < ivBytes.Length ? + secretBytes.Length : + ivBytes.Length + ); + this.iv = ivBytes; + + // + Array.Copy( + secretBytes, + keyBytes, + secretBytes.Length < keyBytes.Length ? + secretBytes.Length : + keyBytes.Length + ); + this.key = keyBytes; + + // Encoding.UTF8.GetBytes (this.secretKey); + } + #endregion + + // + #region Actions ... + public string Encrypt(string plainText) + { + // + validationProvider.NotEmpty(plainText); + + // + var textBytes = Encoding.UTF8.GetBytes(plainText); + var result = EncryptFromBytes(textBytes); + + // + return result; + } + + public string Decrypt(string cipherText) + { + // + validationProvider.NotEmpty(cipherText); + + // + var cipherBytes = Convert.FromBase64String(cipherText); + var result = DecryptFromBytes(cipherBytes); + + // + return result; + } + + public string EncryptFromBytes(byte[] textBytes) + { + // + // Validate Args ... + validationProvider.NotNull(textBytes); + + // Declare the string used to hold + // the decrypted text. + byte[] encryptedBytes = null; + + // Create an RijndaelManaged object + // with the specified key and IV. + using (var rijAlg = new RijndaelManaged()) + { + //Settings + rijAlg.Mode = CipherMode.CBC; + rijAlg.Padding = PaddingMode.PKCS7; + // rijAlg.FeedbackSize = 128; + + rijAlg.Key = key; + rijAlg.IV = iv; + + // + using (var encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV)) + { + encryptedBytes = encryptor.TransformFinalBlock(textBytes, 0, textBytes.Length); + } + } + + // + return Convert.ToBase64String(encryptedBytes); + } + + public string DecryptFromBytes(byte[] cipherBytes) + { + // + // Validate Args ... + validationProvider.NotNull(cipherBytes); + + // Declare the string used to hold + // the decrypted text. + string plaintext = null; + + // Create an RijndaelManaged object + // with the specified key and IV. + using (var rijAlg = new RijndaelManaged()) + { + //Settings + rijAlg.Mode = CipherMode.CBC; + rijAlg.Padding = PaddingMode.PKCS7; + // rijAlg.FeedbackSize = 128; + + rijAlg.Key = key; + rijAlg.IV = iv; + + // Create a decrytor to perform the stream transform. + var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); + + // Create the streams used for decryption. + using (var msDecrypt = new MemoryStream(cipherBytes)) + { + using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + { + using (var srDecrypt = new StreamReader(csDecrypt)) + { + // Read the decrypted bytes from the decrypting stream + // and place them in a string. + plaintext = srDecrypt.ReadToEnd(); + } + } + } + } + + // + return plaintext; + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Stores\XPersistedGrantStore.cs + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using IdentityServer4.EntityFramework.DbContexts; +using IdentityServer4.EntityFramework.Mappers; +using IdentityServer4.Models; +using IdentityServer4.Stores; +using Microsoft.EntityFrameworkCore; + +namespace xIds.Stores +{ + public class XPersistedGrantStore : IPersistedGrantStore + { + private readonly PersistedGrantDbContext dbContext; + + public XPersistedGrantStore(PersistedGrantDbContext dbContext) + { + this.dbContext = dbContext; + } + + public async Task> GetAllAsync(string subjectId) + { + // + var result = (await dbContext.PersistedGrants + .Where(g => g.SubjectId == subjectId) + .AsNoTracking() + .Select(res => res.ToModel()) + .ToListAsync()).AsEnumerable(); + + // + return result; + } + + public async Task GetAsync(string key) + { + // + var result = await dbContext.PersistedGrants + .FirstOrDefaultAsync(g => g.Key == key); + + // + return result.ToModel(); + } + + public async Task RemoveAllAsync(string subjectId, string clientId) + { + // + var items = await dbContext.PersistedGrants + .Where(g => + g.SubjectId == subjectId && + g.ClientId == clientId) + .ToListAsync(); + + // + dbContext.PersistedGrants.RemoveRange(items); + + // + await dbContext.SaveChangesAsync(); + } + + public async Task RemoveAllAsync(string subjectId, string clientId, string type) + { + // + var items = await dbContext.PersistedGrants + .Where(g => + g.SubjectId == subjectId && + g.ClientId == clientId && + g.Type == type) + .ToListAsync(); + + // + dbContext.PersistedGrants.RemoveRange(items); + + // + await dbContext.SaveChangesAsync(); + } + + public async Task RemoveAsync(string key) + { + // + var items = await dbContext.PersistedGrants + .Where(g => g.Key == key) + .ToListAsync(); + + // + dbContext.PersistedGrants.RemoveRange(items); + + // + await dbContext.SaveChangesAsync(); + } + + public async Task IsExistsAsync(string key) + { + return await this.dbContext.PersistedGrants.AnyAsync(g => g.Key == key); + } + + public async Task IsExistsAsync(PersistedGrant grant) + { + return await IsExistsAsync(grant.Key); + } + + public async Task AddOrUpdateAsync(PersistedGrant grant) + { + // + var isExists = await IsExistsAsync(grant); + if (isExists) + { + await UpdateAsync(grant); + } + else + { + await AddAsync(grant); + } + } + + public async Task AddAsync(PersistedGrant grant) + { + // + var item = grant.ToEntity(); + await dbContext.PersistedGrants.AddAsync(item); + await dbContext.SaveChangesAsync(); + } + + public async Task UpdateAsync(PersistedGrant grant) + { + // + var item = grant.ToEntity(); + var entity = dbContext.Attach(item); + entity.State = EntityState.Modified; + await dbContext.SaveChangesAsync(); + } + + public async Task StoreAsync(PersistedGrant grant) + { + await AddOrUpdateAsync(grant); + } + + // + // TODO: Complete this ... + public Task> GetAllAsync(PersistedGrantFilter filter) + { + throw new System.NotImplementedException(); + } + + // + // TODO: Complete this ... + public Task RemoveAllAsync(PersistedGrantFilter filter) + { + throw new System.NotImplementedException(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Validators\XResourceOwnerPasswordValidator.cs + +using System; +using System.Threading.Tasks; +using IdentityServer4.Events; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Validation; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Navigations; +using xIds.Interfaces; +using static IdentityModel.OidcConstants; + +namespace xIds.Validators +{ + public class XResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator + { + private readonly IXIdentityManager identityManager; + private readonly IEventService events; + private readonly ILogger logger; + + public XResourceOwnerPasswordValidator( + IXIdentityManager identityProvider, + IEventService events, + ILogger logger + ) + { + this.identityManager = identityProvider; + this.events = events; + this.logger = logger; + } + + /// + /// Validates the resource owner password credential + /// by providing UserName/Email or PhoneNumber + /// + /// The context. + /// + public virtual async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) + { + // + var clientId = context.Request?.Client?.ClientId; + var xUser = await identityManager.GetUserAsync(context.UserName, true); + + // + // Check Force Data ... + var isForced = true; + var isForcedStr = context.Request?.Raw["force"]; + if (!isForcedStr.IsNullOrEmpty()) + { + isForced = isForcedStr.FromJSON(); + } + + // + XDevice device = null; + var language = context.Request?.Raw["language"]; + var deviceStr = context.Request?.Raw["device"]; + if (deviceStr.IsNullOrEmpty() && isForced) + { + // + logger.LogError($"Device not Found ..."); + + // + await events + .RaiseAsync( + new UserLoginFailureEvent( + xUser.UserName, + XException.InvalidDevice.ToXError().Message, + false, + clientId + )); + return; + } + device = isForced ? deviceStr.FromJSON() : null; + + // + XException exception; + if (!xUser.IsNull()) + { + // + var result = await identityManager + .CheckPasswordSignInAsync( + xUser, + context.Password, + device, + language, + true, + isForced + ); + if (result.Succeeded) + { + // + logger.LogInformation("Credentials validated for username: {username}", xUser.UserName); + + // + // Update User Last Login ... + xUser.LastLogin = DateTime.UtcNow; + await identityManager.UpdateUserAsync(xUser); + + // + await events.RaiseAsync( + new UserLoginSuccessEvent( + xUser.UserName, xUser.Id, xUser.UserName, false, clientId + )); + + // + context.Result = new GrantValidationResult(xUser.Id, AuthenticationMethods.Password); + return; + } + else if (result.IsLockedOut) + { + // + logger.LogInformation("Authentication failed for username: {username}, reason: locked out", xUser.UserName); + + // + exception = XException.AccountLockedOut; + await events + .RaiseAsync( + new UserLoginFailureEvent( + xUser.UserName, + exception.ToXError().Message, + false, + clientId + )); + } + else if (result.IsNotAllowed) + { + // + logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", xUser.UserName); + + // + exception = XException.NotAllowed; + await events + .RaiseAsync( + new UserLoginFailureEvent( + xUser.UserName, + exception.ToXError().Message, + false, + clientId + )); + } + else + { + // + logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", xUser.UserName); + + // + exception = XException.LoginFailed; + await events + .RaiseAsync( + new UserLoginFailureEvent( + xUser.UserName, + exception.ToXError().Message, + false, + clientId + )); + + // + + } + } + else + { + // + logger.LogInformation("No user found matching username: {username}", xUser.UserName); + + // + exception = XException.InvalidUserName; + await events + .RaiseAsync( + new UserLoginFailureEvent( + xUser.UserName, + exception.ToXError().Message, + false, + clientId + )); + } + + // + Exception ecs = exception.ToException(); + if (exception == XException.AccountLockedOut && xUser.LockoutEnd.HasValue) + { + // + var passedTime = xUser.LockoutEnd.Value.UtcDateTime; + ecs = XException.AccountLockedOut + .AddContentToException(passedTime.ToString()); + } + + // + context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, ecs.Message); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Program.cs + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace xIds +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xIds\Startup.cs + +using System; +using System.IO; +using System.Reflection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using xCommons.Configurations; +using xCommons.Extensions; +using xStorageService.DI; +using xIds.Constants; +using xIds.DI; +using xIds.Interfaces; +using xIds.Providers; + +namespace xIds +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public void ConfigureServices(IServiceCollection services) + { + // + // Register Validation Provider and all XCommons Module Services ... + services.AddXCommons(); + + // + // Register App Configuration ... + services.AddXAppConfiguration(Configuration); + var appConfiguration = services.GetRegisteredService(); + + // + // Register XDataService Configuration ... + services.AddXDataServiceConfiguration(Configuration, ConnectionStringNames.IDENTITY_CONNECTION_NAME); + + // + // Register Allowed Origins ... + services.AddXCors(appConfiguration.AllowedOrigins); + + // + // Register Swagger ... + var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + services.AddXSwagger(Configuration, xmlFilePath: xmlPath); + + // + // Register IdentityServer to DI ... + // services.AddInMemoryXIdentityServer (Configuration); + services.AddEfSupportXIdentityServer(Configuration); + + // + services.AddControllers() + .AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore); + + // + // Register Authentication ... + services.AddXIdentityServerAuthentication(Configuration); + + // + // Register Authorization ... + services.AddXAuthorization(); + + // + // Register Security Provider ... + services.AddSingleton(); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger logger) + { + // + if (env.IsDevelopment()) + { + // + app.UseDeveloperExceptionPage(); + + // + // app.UseXRequestLogger(); + } + + // + // Use Swagger Middleware ... + app.UseXSwagger(); + + // + app.UseHttpsRedirection(); + app.UseXForwardOptions(); + + // + // Using Cors ... + app.UseXCors(); + + // + // Force App to Use IdentityServer ... + app.UseXIdentityServer(logger); + + // + // Force app to Use Storage Service ... + app.UseXStorageService(); + + // + app.UseRouting(); + + // + app.UseAuthentication(); + app.UseAuthorization(); + + // + app.UseEndpoints(endpoints => + { + endpoints.MapDefaultControllerRoute(); + }); + } + } +} diff --git a/Modules/xDataService b/Modules/xDataService index 97a6fa9..ec6d99f 160000 --- a/Modules/xDataService +++ b/Modules/xDataService @@ -1 +1 @@ -Subproject commit 97a6fa92ed386475ce671b922d2c79f17e3526df +Subproject commit ec6d99f979ba5b495fed369b706c87742f1dc898