From ac168ed7c1cb5baaa672c041685af262a5e5c097 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Wed, 27 May 2026 13:55:24 +0330 Subject: [PATCH] add base requirements ... --- DI/XDIHelperExtension.cs | 100 ++++ Hubs/.gitkeep | 0 Hubs/XTermsHub.cs | 107 ++++ Interfaces/IXTermsAndComditionsController.cs | 82 ++++ Interfaces/IXTermsAndComditionsProvider.cs | 95 ++++ Providers/XTermsAndComditionsProvider.cs | 490 +++++++++++++++++++ 6 files changed, 874 insertions(+) create mode 100644 DI/XDIHelperExtension.cs create mode 100644 Hubs/.gitkeep create mode 100644 Hubs/XTermsHub.cs create mode 100644 Interfaces/IXTermsAndComditionsController.cs create mode 100644 Interfaces/IXTermsAndComditionsProvider.cs create mode 100644 Providers/XTermsAndComditionsProvider.cs diff --git a/DI/XDIHelperExtension.cs b/DI/XDIHelperExtension.cs new file mode 100644 index 0000000..6580181 --- /dev/null +++ b/DI/XDIHelperExtension.cs @@ -0,0 +1,100 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Configurations; +using xCommons.Extensions; +using xExceptions.Constants; +using xStringService.Interfaces.Dtos; +using xTermsAndConditionsService.Interfaces; +using xTermsAndConditionsService.Providers; + +namespace xTermsAndConditionsService.DI +{ + public static class XDIHelperExtension + { + /// + /// Register TermsConditions Provider ... + /// + /// + /// + public static void AddXTermsConditionsService( + this IServiceCollection services, + ServiceLifetime lifeTime = ServiceLifetime.Transient + ) + { + // + AddTermsConditionsService( + services: services, + lifeTime: lifeTime + ); + } + + // + #region Private ... + /// + /// a LogTag for Service ... + /// + private static string XLogTag = "XTermsConditionsService"; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Add Terms and Conditions Provider Service ... + /// + /// + /// + private static void AddTermsConditionsService( + IServiceCollection services, + ServiceLifetime lifeTime = ServiceLifetime.Transient + ) + { + // + var exception = XException.InvalidArgs.ToException(); ; + + // + // Services ... + var isServicesExists = !services.IsNull(); + if (!isServicesExists) + { + // + Log("AddTermsConditions failed, services not provided ..."); + throw exception; + } + + // + // Lifetime ... + var isLifetimeExists = !lifeTime.IsNull(); + if (!isLifetimeExists) + { + // + Log("AddTermsConditions failed, lifetime not provided ..."); + throw exception; + } + + // + // Check Dependencies Exists ... + var isDependenciesPassed = !services + .GetRegisteredService() + .IsNull() && + !services.GetRegisteredService() + .IsNull(); + if (!isDependenciesPassed) + { + // + Log("AddTermsConditions failed due Dependencies Issues, IXStringRepository or XAppConfiguration not provided ..."); + throw exception; + } + + // + // Register Resource Providers ... + services.Add(new ServiceDescriptor(typeof(IXTermsAndComditionsProvider), typeof(XTermsAndComditionsProvider), lifeTime)); + } + #endregion + } +} \ No newline at end of file diff --git a/Hubs/.gitkeep b/Hubs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Hubs/XTermsHub.cs b/Hubs/XTermsHub.cs new file mode 100644 index 0000000..497e855 --- /dev/null +++ b/Hubs/XTermsHub.cs @@ -0,0 +1,107 @@ +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 + } +} \ No newline at end of file diff --git a/Interfaces/IXTermsAndComditionsController.cs b/Interfaces/IXTermsAndComditionsController.cs new file mode 100644 index 0000000..32ff929 --- /dev/null +++ b/Interfaces/IXTermsAndComditionsController.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using xModels.Base; + +namespace xTermsAndConditionsService.Interfaces +{ + /// + /// an Interface for Describing Terms and Conditions Management Controller Actions ... + /// + public interface IXTermsAndComditionsController + { + /// + /// Retrieved all Exists Languages Terms and Conditions ... + /// + /// + [HttpGet("Terms/Languages")] + Task>> TermsLanguages( + CancellationToken cancellationToken = default + ); + + /// + /// Check Has Terms and Conditions based on Specified Language ... + /// + /// if null, used Default Language ... + /// + [HttpGet("Terms/{language}/Has")] + Task> HasTerms( + [FromRoute] string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Terms and Conditions for Specified Language ... + /// + /// if null, used Default Language ... + /// + [HttpGet("Terms/{language}")] + Task> GetTerms( + [FromRoute] string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Terms and Conditions for Specified Language ... + /// + /// + /// + /// + [HttpPost("Terms/{language}")] + Task> AddTerms( + [FromRoute] string language, + [FromBody] XBaseValueContainer terms, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Terms and Conditions of Specified Language ... + /// + /// + /// + [HttpDelete("Terms/{language}")] + Task> RemoveTerms( + [FromRoute] string language, + CancellationToken cancellationToken = default + ); + + /// + /// Update Terms and Conditions of Specified Language ... + /// + /// + /// + /// + [HttpPut("Terms/{language}")] + Task> UpdateTerms( + [FromRoute] string language, + [FromBody] XBaseValueContainer terms, + CancellationToken cancellationToken = default + ); + } +} \ No newline at end of file diff --git a/Interfaces/IXTermsAndComditionsProvider.cs b/Interfaces/IXTermsAndComditionsProvider.cs new file mode 100644 index 0000000..0a840e9 --- /dev/null +++ b/Interfaces/IXTermsAndComditionsProvider.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using xIdentityModels.Models; + +namespace xTermsAndConditionsService.Interfaces +{ + /// + /// an Interface for Describing Terms and Conditions Management Provided Actions ... + /// + public interface IXTermsAndComditionsProvider + { + #region Actions ... + /// + /// Retrieved all Exists Languages Terms and Conditions ... + /// + /// + /// + Task> TermsLanguages( + CancellationToken cancellationToken = default + ); + + /// + /// Check Has Terms and Conditions based on Specified Language ... + /// + /// if null, used Default Language ... + /// + /// + Task HasTerms( + string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Retrieve Terms and Conditions for Specified Language ... + /// + /// if null, used Default Language ... + /// + /// + Task GetTerms( + string language = null, + CancellationToken cancellationToken = default + ); + + /// + /// Add Terms and Conditions for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + Task AddTerms( + string language, + string terms, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Remove Terms and Conditions of Specified Language ... + /// + /// + /// + /// + /// + /// + Task RemoveTerms( + string language, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + + /// + /// Update Terms and Conditions of Specified Language ... + /// + /// + /// + /// + /// + /// + /// + Task UpdateTerms( + string language, + string terms, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ); + #endregion + } +} \ No newline at end of file diff --git a/Providers/XTermsAndComditionsProvider.cs b/Providers/XTermsAndComditionsProvider.cs new file mode 100644 index 0000000..31b09a9 --- /dev/null +++ b/Providers/XTermsAndComditionsProvider.cs @@ -0,0 +1,490 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using xCommons.Configurations; +using xCommons.Extensions; +using xExceptions.Constants; +using xIdentityModels.Extensions; +using xIdentityModels.Models; +using xPushService.Constants; +using xStringService.Interfaces.Dtos; +using xStringService.Models.Dtos; +using xTermsAndConditionsService.Hubs; +using xTermsAndConditionsService.Interfaces; + +namespace xTermsAndConditionsService.Providers +{ + /// + /// an Implementation of Terms and Conditions Management Provided Actions ... + /// + public class XTermsAndComditionsProvider : IXTermsAndComditionsProvider + { + // + #region Props ... + private readonly string termsId; + private readonly string[] allowedRoles; + private readonly IHubContext hub; + private readonly XAppConfiguration appConfiguration; + private readonly IXStringServiceProvider stringProvider; + #endregion + + // + #region Constructor ... + public XTermsAndComditionsProvider( + XAppConfiguration appConfiguration, + IXStringServiceProvider stringProvider, + IHubContext hub = null, + string termsId = "terms" + ) + { + // + this.hub = hub; + this.termsId = termsId; + this.stringProvider = stringProvider; + this.appConfiguration = appConfiguration; + + // + // Specified roles for Handling Dangerouse Actions ... + this.allowedRoles = new string[] + { + "admin" + }; + } + #endregion + + // + #region Actions ... + /// + /// Retrieved all Exists Languages Terms and Conditions ... + /// + /// + /// + public async Task> TermsLanguages( + CancellationToken cancellationToken = default + ) + { + // + var result = (await stringProvider + .FindManyAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken, + predicate: x => x.ResourceTitle == termsId, + orderBuilder: x => x.OrderBy(y => y.ResourceTitle) + )) + .Select(x => x.Language); + return result; + } + + /// + /// Check Has Terms and Conditions based on Specified Language ... + /// + /// if null, used Default Language ... + /// + /// + public async Task HasTerms( + string language = null, + CancellationToken cancellationToken = default + ) + { + // + // Normalize ... + if (language.IsNullOrEmpty()) + { + language = appConfiguration.DefaultLanguage; + } + + // + var result = !(await GetString( + language: language, + cancellationToken: cancellationToken + )) + .IsNullOrDefault(); + + // + return result; + } + + /// + /// Retrieve Terms and Conditions for Specified Language ... + /// + /// if null, used Default Language ... + /// + /// + public async Task GetTerms( + string language = null, + CancellationToken cancellationToken = default + ) + { + // + var result = string.Empty; + var item = await GetString( + language: language, + cancellationToken: cancellationToken + ); + + // + if (!item.IsNullOrDefault()) + { + result = item.TranslatedValue; + } + + // + return result; + } + + /// + /// Add Terms and Conditions for Specified Language ... + /// + /// + /// + /// + /// + /// + /// + public async Task AddTerms( + string language, + string terms, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !language.IsNullOrEmpty() && + !terms.IsNullOrEmpty() && + !userInfo.IsNullOrDefault() && + language != appConfiguration.DefaultLanguage; + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Exists ... + isValid = await HasTerms( + language: language, + cancellationToken: cancellationToken + ); + if (isValid) + { + XException.Duplicate.Throw(); + } + + // + // Check Permission ... + isValid = userInfo.HasAccess(allowedRoles); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + // Add Terms ... + XStringDto item = null; + try + { + // + item = new XStringDto + { + Language = language, + ResourceTitle = termsId, + TranslatedValue = terms, + }; + item = await stringProvider.AddAsync( + item: item, + saveChanges: true, + cancellationToken: cancellationToken + ); + } + catch { } + + // + var result = string.Empty; + if (!item.IsNullOrDefault()) + { + result = item.TranslatedValue; + } + + // + if (!result.IsNullOrEmpty()) + { + // + await SendPush( + connectionId: connectionId, + cancellationToken: cancellationToken, + payLoad: item.ToJSON(camelCase: true), + action: XBaseEntityHubAction.Add.GetStringValue() + ); + } + + // + return result; + } + + /// + /// Remove Terms and Conditions of Specified Language ... + /// + /// + /// + /// + /// + /// + /// + public async Task RemoveTerms( + string language, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var isValid = + !language.IsNullOrEmpty() && + !userInfo.IsNullOrDefault() && + language != appConfiguration.DefaultLanguage; + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Exists ... + var item = await GetString( + language: language, + cancellationToken: cancellationToken + ); + isValid = !item.IsNullOrDefault(); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Check Permission ... + isValid = userInfo.HasAccess(allowedRoles); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + item = await stringProvider.RemoveAsync( + id: item.Id, + softDelete: false, + saveChanges: true, + cancellationToken: cancellationToken + ); + } + catch + { + item = null; + } + + // + var result = !item.IsNullOrDefault(); + if (result) + { + // + await SendPush( + connectionId: connectionId, + cancellationToken: cancellationToken, + payLoad: item.ToJSON(camelCase: true), + action: XBaseEntityHubAction.Delete.GetStringValue() + ); + } + + // + return result; + } + + /// + /// Update Terms and Conditions of Specified Language ... + /// + /// + /// + /// + /// + /// + /// + public async Task UpdateTerms( + string language, + string terms, + XUserClaimsInfoDto userInfo = null, + string connectionId = null, + CancellationToken cancellationToken = default + ) + { + // + var isValid = + !terms.IsNullOrEmpty() && + !language.IsNullOrEmpty() && + !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Exists ... + var item = await GetString( + language: language, + cancellationToken: cancellationToken + ); + isValid = !item.IsNullOrDefault(); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Check Permission ... + isValid = userInfo.HasAccess(allowedRoles); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + item.TranslatedValue = terms; + item = await stringProvider.UpdateAsync( + id: item.Id, + item: item, + saveChanges: true, + cancellationToken: cancellationToken + ); + } + catch + { + item = null; + } + + // + var result = !item.IsNullOrDefault(); + if (result) + { + // + await SendPush( + connectionId: connectionId, + cancellationToken: cancellationToken, + payLoad: item.ToJSON(camelCase: true), + action: XBaseEntityHubAction.Update.GetStringValue() + ); + } + + // + 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(), + }; + + // + // 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 + + // + #region Private ... + /// + /// Retrieve Terms and Conditions XString Entity ... + /// + /// + /// + /// + private async Task GetString( + string language = null, + CancellationToken cancellationToken = default + ) + { + // + // Normalize ... + if (language.IsNullOrEmpty()) + { + language = appConfiguration.DefaultLanguage; + } + + // + // Retrieve ... + XStringDto result = null; + try + { + // + result = await stringProvider.FindOneAsync( + includeBuilder: null, + ignoreSoftDeleteds: true, + predicate: x => + x.Language == language && + x.ResourceTitle == termsId, + cancellationToken: cancellationToken + ); + } + catch + { + } + + // + return result; + } + #endregion + } +} \ No newline at end of file