diff --git a/Constants/xWebinarServiceConstants.cs b/Constants/xWebinarServiceConstants.cs index 6135890..2b727fc 100644 --- a/Constants/xWebinarServiceConstants.cs +++ b/Constants/xWebinarServiceConstants.cs @@ -1,3 +1,16 @@ +using xExceptions.Attributes; + namespace xWebinarService.Constants { public class xWebinarServiceConstants { } + + /// + /// Webinar Type ... + /// + public enum XWebinarType + { + [StringValue("Public")] + Public = 0, + [StringValue("Private")] + Private = 1 + } } \ No newline at end of file diff --git a/DI/XDIHelperExtension.cs b/DI/XDIHelperExtension.cs index f0aba89..160ac05 100644 --- a/DI/XDIHelperExtension.cs +++ b/DI/XDIHelperExtension.cs @@ -1,3 +1,117 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xExceptions.Constants; +using xWebinarService.Interfaces; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Providers; + namespace xWebinarService.DI { - public static class XDIHelperExtension { } + public static class XDIHelperExtension + { + /// + /// Register Module Provided Service on DI + /// + /// + /// + public static void AddXWebinarService( + this IServiceCollection services, + ServiceLifetime lifeTime + ) + { + // + AddWebinarService( + services, + lifeTime + ); + } + + // + #region Private ... + /// + /// a LogTag for XWebinarService ... + /// + private static string XLogTag = "XWebinarService"; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Push Service ... + /// + /// + /// + private static void AddWebinarService( + IServiceCollection services, + ServiceLifetime lifeTime + ) + { + // + var exception = XException.InvalidArgs.ToException(); ; + + // + // Services ... + var isServicesExists = !services.IsNull(); + if (!isServicesExists) + { + // + Log("AddWebinarService failed, services not provided ..."); + throw exception; + } + + // + // Lifetime ... + var isLifetimeExists = !lifeTime.IsNull(); + if (!isLifetimeExists) + { + // + Log("AddWebinarService failed, lifetime not provided ..."); + throw exception; + } + + // + // Check String Repository Exists ... + var isWebinarRepositoryExists = !services + .GetRegisteredService() + .IsNull() && + !services.GetRegisteredService() + .IsNull(); + if (!isWebinarRepositoryExists) + { + // + Log("AddWebinarService failed, IXWebinarRepository or IXWebinarSubscriberRepository not provided ..."); + throw exception; + } + + // + // Check WEBRTC Hub Exists ... + var isWebRTCHubExists = !services.GetRegisteredService().IsNull(); + if (!isWebRTCHubExists) + { + // + Log("AddWebinarService failed, XWebinarHub not provided ..."); + throw exception; + } + + // + // Register Resource Providers ... + services.Add(new ServiceDescriptor(typeof(IXWebinarTitleResourceProvider), typeof(XWebinarTitleResourceProvider), lifeTime)); + services.Add(new ServiceDescriptor(typeof(IXWebinarDescriptionResourceProvider), typeof(XWebinarDescriptionResourceProvider), lifeTime)); + + // + // Register Main Provider ... + services.Add(new ServiceDescriptor(typeof(IXWebinarProvider), typeof(XWebinarProvider), lifeTime)); + + // + Log("AddWebinarService Succeed ..."); + } + #endregion + + } } \ No newline at end of file diff --git a/Interfaces/IXWebinarDescriptionResourceProvider.cs b/Interfaces/IXWebinarDescriptionResourceProvider.cs new file mode 100644 index 0000000..68169c5 --- /dev/null +++ b/Interfaces/IXWebinarDescriptionResourceProvider.cs @@ -0,0 +1,7 @@ +using xStringService.Interfaces; + +namespace xWebinarService.Interfaces +{ + public interface IXWebinarDescriptionResourceProvider : IXBaseStringResourceProvider + { } +} \ No newline at end of file diff --git a/Interfaces/IXWebinarHub.cs b/Interfaces/IXWebinarHub.cs new file mode 100644 index 0000000..f62a379 --- /dev/null +++ b/Interfaces/IXWebinarHub.cs @@ -0,0 +1,19 @@ +using xPushService.Base; +using xPushService.Interfaces; + +namespace xWebinarService.Interfaces +{ + public abstract class XWebinarHub : XBaseWebRTCHub + { + protected XWebinarHub( + IXPushGroupStore groupStore, + IXPushConnectionStore connectionStore, + IXWebRTCConnectionStore webRTCConnectionStore + ) : base( + groupStore, + connectionStore, + webRTCConnectionStore + ) + { } + } +} \ No newline at end of file diff --git a/Interfaces/IXWebinarProvider.cs b/Interfaces/IXWebinarProvider.cs new file mode 100644 index 0000000..819dd5c --- /dev/null +++ b/Interfaces/IXWebinarProvider.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using xDataService.Configuration; +using xIdentityService.Interfaces; +using xModels.Dtos; +using xWebinarService.Interfaces.Entities; +using xWebinarService.Models.Dtos; +using xWebinarService.Models.Entities; + +namespace xWebinarService.Interfaces +{ + public interface IXWebinarProvider + { + // + #region Props ... + IXIdentityProvider IdentityProvider { get; } + IXWebinarRepository WebinarRepository { get; } + XDataServiceConfiguration DataSercviceConfiguration { get; } + IXWebinarTitleResourceProvider TitleResourceProvider { get; } + IXWebinarSubscriberRepository WebinarSubscriberRepository { get; } + IXWebinarDescriptionResourceProvider DescriptionResourceProvider { get; } + #endregion + + // + #region Tools ... + /// + /// Converts Specified XWebinar Entity to it's Corresponding Dto Presentation + /// and also Fill Owner and Title/Description Resources ... + /// + /// + /// + Task ToXWebinarDto(XWebinar model); + + /// + /// Converts Specified WebinarSubscriber Entity to it's Dto Presentation ... + /// + /// + /// + Task ToXWebinarSubscriberDto(XWebinarSubscriber model); + #endregion + + // + #region Actions ... + /// + /// Create Webinar ... + /// + /// + /// + Task CreateWebinar(XWebinarDto item); + + /// + /// Update a Webinar ... + /// + /// + /// + Task UpdateWebinar(XWebinarDto item); + + /// + /// Retrieve Specified Webinar ... + /// + /// + /// + Task GetWebinar(Guid id); + + /// + /// Retrieve all Exists Webinars ... + /// + /// + Task> GetWebinars(); + + /// + /// Query Webinars ... + /// + /// + /// + Task> QueryWebinars(XQuery query); + + /// + /// Remove Specified Webinar ... + /// + /// + /// + /// + Task RemoveWebinar( + Guid webinarId, + string requesterUserSelectByParam = null + ); + #endregion + + // + #region WebinarSubscriber ... + /// + /// Subscribe a User to Webinar ... + /// + /// + /// + /// + /// + Task Subscribe( + Guid webinarId, + string subscriberId, + string requesterUserSelectByParam = null + ); + + /// + /// Check Specified User Subscribed on a Webinar or not ... + /// + /// + /// + /// + Task IsSubscribed( + Guid webinarId, + string subscriberId + ); + + /// + /// Unsubscribe Specified User from a Specified Webinar ... + /// + /// + /// + /// + /// + Task Unsubscribe( + Guid webinarId, + string subscriberId, + string requesterUserSelectByParam = null + ); + + /// + /// Retrieve Specified Subscriber ... + /// + /// + /// + /// + /// + Task GetSubscriber( + Guid webinarId, + string subscriberId, + string requesterUserSelectByParam = null + ); + + /// + /// Get all Specified Webinars Subscribers ... + /// + /// + /// + /// + Task> GetSubscribers( + Guid webinarId, + string requesterUserSelectByParam = null + ); + + /// + /// Query Specified Webinar's Subscribers ... + /// + /// + /// + /// + /// + Task> QuerySubscribers( + Guid webinarId, + XQuery query, + string requesterUserSelectByParam = null + ); + #endregion + + } +} \ No newline at end of file diff --git a/Interfaces/IXWebinarTitleResourceProvider.cs b/Interfaces/IXWebinarTitleResourceProvider.cs new file mode 100644 index 0000000..a112d22 --- /dev/null +++ b/Interfaces/IXWebinarTitleResourceProvider.cs @@ -0,0 +1,7 @@ +using xStringService.Interfaces; + +namespace xWebinarService.Interfaces +{ + public interface IXWebinarTitleResourceProvider : IXBaseStringResourceProvider + { } +} \ No newline at end of file diff --git a/Models/Dtos/XWebinarDto.cs b/Models/Dtos/XWebinarDto.cs index a93fda7..cf4e578 100644 --- a/Models/Dtos/XWebinarDto.cs +++ b/Models/Dtos/XWebinarDto.cs @@ -1,20 +1,20 @@ using System; using xModels.Base; using xModels.Dtos; +using xWebinarService.Constants; namespace xWebinarService.Models.Dtos { public class XWebinarDto : XBaseDto { public Guid Id { get; set; } - public XResourceDto Title { get; set; } public XResourceDto Description { get; set; } - public string OwnerId { get; set; } public XPersonDto Owner { get; set; } - + public XWebinarType Type { get; set; } public DateTime CreatedOn { get; set; } + public bool Enabled { get; set; } } public class XWebinarSubscriberDto : XBaseDto diff --git a/Models/Entities/XWebinar.cs b/Models/Entities/XWebinar.cs index cedfa60..b98fc9e 100644 --- a/Models/Entities/XWebinar.cs +++ b/Models/Entities/XWebinar.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; using xModels.Base; +using xWebinarService.Constants; namespace xWebinarService.Models.Entities { @@ -32,12 +33,23 @@ namespace xWebinarService.Models.Entities [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; } } /// diff --git a/Providers/XWebinarDescriptionResourceProvider.cs b/Providers/XWebinarDescriptionResourceProvider.cs new file mode 100644 index 0000000..aad3fec --- /dev/null +++ b/Providers/XWebinarDescriptionResourceProvider.cs @@ -0,0 +1,23 @@ +using xCommons.Configurations; +using xStringService.Base; +using xStringService.Interfaces; +using xWebinarService.Interfaces; + +namespace xWebinarService.Providers +{ + /// + /// a Servide to Provides Requirements for XWebinar Description Resources ... + /// + public class XWebinarDescriptionResourceProvider : XBaseStringResourceProvider, IXWebinarDescriptionResourceProvider + { + public XWebinarDescriptionResourceProvider( + IXStringProvider provider, + XAppConfiguration appConfiguration + ) : base( + "XWEBD", + provider, + appConfiguration + ) + { } + } +} \ No newline at end of file diff --git a/Providers/XWebinarProvider.cs b/Providers/XWebinarProvider.cs index 4d702ee..90915a0 100644 --- a/Providers/XWebinarProvider.cs +++ b/Providers/XWebinarProvider.cs @@ -1,5 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using xCommons.Extensions; +using xDataService.Configuration; +using xDataService.Extensions; +using xExceptions.Constants; +using xIdentityModels.Dtos; +using xIdentityService.Extensions; using xIdentityService.Interfaces; +using xModels.Dtos; +using xWebinarService.Constants; +using xWebinarService.Interfaces; using xWebinarService.Interfaces.Entities; using xWebinarService.Models.Dtos; using xWebinarService.Models.Entities; @@ -9,47 +21,1008 @@ namespace xWebinarService.Providers /// /// Implement all Webinar Related Things here ... /// - public class XWebinarProvider + public class XWebinarProvider : IXWebinarProvider { // #region Props ... - public IXIdentityProvider IdentityProvider; - public IXWebinarRepository WebinarRepository; - public IXWebinarSubscriberRepository WebinarSubscriberRepository; - #endregion; + public IXIdentityProvider IdentityProvider { get; } + public IXWebinarRepository WebinarRepository { get; } + public XDataServiceConfiguration DataSercviceConfiguration { get; } + public IXWebinarTitleResourceProvider TitleResourceProvider { get; } + public IXWebinarSubscriberRepository WebinarSubscriberRepository { get; } + public IXWebinarDescriptionResourceProvider DescriptionResourceProvider { get; } + #endregion // #region Constructor ... public XWebinarProvider( IXIdentityProvider identityProvider, IXWebinarRepository webinarRepository, - IXWebinarSubscriberRepository webinarSubscriberRepository + XDataServiceConfiguration dataSercviceConfiguration, + IXWebinarTitleResourceProvider titleResourceProvider, + IXWebinarSubscriberRepository webinarSubscriberRepository, + IXWebinarDescriptionResourceProvider descriptionResourceProvider ) { // - this.IdentityProvider = identityProvider; - this.WebinarRepository = webinarRepository; - this.WebinarSubscriberRepository = webinarSubscriberRepository; + IdentityProvider = identityProvider; + WebinarRepository = webinarRepository; + DataSercviceConfiguration = dataSercviceConfiguration; + TitleResourceProvider = titleResourceProvider; + WebinarSubscriberRepository = webinarSubscriberRepository; + DescriptionResourceProvider = descriptionResourceProvider; } #endregion // #region Tools ... + /// + /// Converts Specified XWebinar Entity to it's Corresponding Dto Presentation + /// and also Fill Owner and Title/Description Resources ... + /// + /// + /// public async Task ToXWebinarDto(XWebinar model) { // - XWebinarDto result = null; + // Validate ... + bool isValid = + !model.IsNullOrDefault() && + !model.Title.IsNullOrEmpty() && + !model.OwnerId.IsNullOrEmpty() && + !model.Description.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } // - result.Id = model.Id; - result.OwnerId = model.OwnerId; - result.CreatedOn = model.CreatedOn; + var result = new XWebinarDto + { + // + // Set Common Properties ... + Id = model.Id, + Type = model.Type, + OwnerId = model.OwnerId, + CreatedOn = model.CreatedOn, + + // + // Fill Resources ... + Title = await TitleResourceProvider.GetResource(model.Id.ToString()), + Description = await DescriptionResourceProvider.GetResource(model.Id.ToString()) + }; // - // Fill Resources ... + // Prepare Inner API Providers Device Dto ... + var device = await IdentityProvider.GetDevice(); + var userInfo = await IdentityProvider.GetUserInfo( + device: device, + userSelectByParam: model.OwnerId + ); + isValid = !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.NotFound.Throw(); + } // - // Fill Owner ... + result.Owner = userInfo.ToXPersonDto(); + + // + return result; + } + + /// + /// Converts Specified WebinarSubscriber Entity to it's Dto Presentation ... + /// + /// + /// + public async Task ToXWebinarSubscriberDto(XWebinarSubscriber model) + { + // + bool isValid = + !model.IsNull() && + !model.SubscriberId.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve User Info ... + var device = await IdentityProvider.GetDevice(); + var userInfo = await IdentityProvider.GetUserInfo( + device: device, + userSelectByParam: model.SubscriberId + ); + isValid = !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + var result = new XWebinarSubscriberDto + { + Id = model.Id, + SubscriberId = model.SubscriberId, + SubscribedOn = model.SubscribedOn, + Subscriber = userInfo.ToXPersonDto(), + }; + + // + return result; + } + #endregion + + // + #region Actions ... + /// + /// Create Webinar ... + /// + /// + /// + public async Task CreateWebinar(XWebinarDto item) + { + // + // Validate ... + bool isValid = + !item.IsNull() && + !item.Title.IsNull() && + !item.Description.IsNull() && + !item.OwnerId.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Title and Descriptions Has Value ... + isValid = + item.Title.Locales.HasChild() && + item.Description.Locales.HasChild() && + item.Title.Locales.All(l => !l.Value.IsNullOrEmpty()) && + item.Description.Locales.All(l => !l.Value.IsNullOrEmpty()); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Prepare Temp Webinar Entity ... + string tmpValue = Guid.NewGuid().ToString(); + var entity = new XWebinar + { + Type = item.Type, + OwnerId = item.OwnerId, + Title = "T_" + tmpValue, + CreatedOn = DateTime.UtcNow, + Description = "D_" + tmpValue, + }; + entity = await WebinarRepository.AddAsync(entity); + isValid = + !entity.IsNullOrDefault() && + !entity.Id.IsDefaultGuid(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + + // + // Use Entity ID as Resources Suffix ... + Guid suffixGuid = entity.Id; + string suffix = suffixGuid.ToString(); + var titleResource = await TitleResourceProvider.AddResource( + suffix: suffix, + item: item.Title + ); + var descriptionResource = await DescriptionResourceProvider.AddResource( + suffix: suffix, + item: item.Description + ); + isValid = + // + // Title Resource Validation ... + !titleResource.IsNullOrDefault() && + titleResource.Locales.HasChild() && + !titleResource.Resource.IsNullOrEmpty() && + // + // Description Resource Validation ... + !descriptionResource.IsNullOrDefault() && + descriptionResource.Locales.HasChild() && + !descriptionResource.Resource.IsNullOrEmpty() + ; + if (!isValid) + { + // + // Rollback ... + try + { + // + entity.Id = suffixGuid; + await TitleResourceProvider.Remove(suffix); + await WebinarRepository.RemoveAsync(entity); + await DescriptionResourceProvider.Remove(suffix); + } + catch { } + + // + XException.ActionFailed.Throw(); + } + + // + // Update Title/Description Resources ... + entity.Title = titleResource.Resource; + entity.Description = descriptionResource.Resource; + entity = await WebinarRepository.UpdateAsync(entity.Id, entity); + isValid = !entity.IsNullOrDefault(); + if (!isValid) + { + // + // Rollback ... + try + { + // + entity.Id = suffixGuid; + await TitleResourceProvider.Remove(suffix); + await WebinarRepository.RemoveAsync(entity); + await DescriptionResourceProvider.Remove(suffix); + } + catch { } + + // + XException.ActionFailed.Throw(); + } + + // + // Convert Entity to Dto ... + var result = await ToXWebinarDto(entity); + return result; + } + + /// + /// Update a Webinar ... + /// + /// + /// + public async Task UpdateWebinar(XWebinarDto item) + { + // + // Validate ... + bool isValid = + !item.Id.IsNull() && + + !item.IsNullOrDefault() && + !item.Id.IsDefaultGuid() && + !item.OwnerId.IsNullOrEmpty() && + !item.Title.IsNullOrDefault() && + !item.Description.IsNullOrDefault() && + // + // Title Validation ... + item.Title.Locales.HasChild() && + !item.Title.Resource.IsNullOrEmpty() && + item.Title.Locales.All(l => !l.IsNullOrDefault() && !l.Value.IsNullOrEmpty() && !l.Language.IsNullOrEmpty()) && + // + // Description Validation ... + item.Description.Locales.HasChild() && + !item.Description.Resource.IsNullOrEmpty() && + item.Description.Locales.All(l => !l.IsNullOrDefault() && !l.Value.IsNullOrEmpty() && !l.Language.IsNullOrEmpty()) + ; + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Item Exists ... + isValid = await WebinarRepository.IsExistsAsync(item.Id); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Retrieve Entity ... + var entity = await WebinarRepository.GetAsync(item.Id); + + // + // Check if Type Change Update it ... + if (entity.Type != item.Type) + { + // + entity.Type = item.Type; + entity = await WebinarRepository.UpdateAsync(entity.Id, entity); + } + + // + // Retrieve Entity Dto ... + var entityDto = await ToXWebinarDto(entity); + + // + // Exclude Must Remove items ... + var mustRemoveTitleLocals = entityDto.Title.Locales.Where(e => + !item.Title.Locales.Any(l => + l.Language.ToNormalString() == e.Language.ToNormalString()) + ); + var mustRemoveDescriptionLocals = entityDto.Description.Locales.Where(e => + !item.Description.Locales.Any(l => + l.Language.ToNormalString() == e.Language.ToNormalString()) + ); + + // + // Remove Specified Locales ... + + // + // Titles ... + foreach (var locale in mustRemoveTitleLocals) + { + // + try + { + // + await TitleResourceProvider.Remove( + resource: entity.Title, + language: locale.Language + ); + } + catch { } + } + + // + // Descriptions ... + foreach (var locale in mustRemoveDescriptionLocals) + { + // + try + { + // + await DescriptionResourceProvider.Remove( + resource: entity.Description, + language: locale.Language + ); + } + catch { } + } + + // + // Add Or Update Locales ... + + // + // Title ... + var titleAddedLocales = await TitleResourceProvider.AddOrUpdate( + resource: entity.Title, + locales: entityDto.Title.Locales + ); + + // + // Description ... + var descriptionAddedLocales = await DescriptionResourceProvider.AddOrUpdate( + resource: entity.Description, + locales: entityDto.Description.Locales + ); + + // + var result = await ToXWebinarDto(entity); + + // + return result; + } + + /// + /// Retrieve Specified Webinar ... + /// + /// + /// + public async Task GetWebinar(Guid id) + { + // + var entity = await WebinarRepository.GetAsync(id); + if (entity.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + var result = await ToXWebinarDto(entity); + return result; + } + + /// + /// Retrieve all Exists Webinars ... + /// + /// + public async Task> GetWebinars() + { + // + var list = await WebinarRepository.GetAllAsync(); + var result = await list.SelectAsync(async l => await ToXWebinarDto(l)); + + // + return result; + } + + /// + /// Query Webinars ... + /// + /// + /// + public async Task> QueryWebinars(XQuery query) + { + // + // Validate ... + if (query.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + query = query.NormalizeQuery(DataSercviceConfiguration); + + // + // Since in Resourceable Entities we have to Search on Locales + // we Must Implement Senario Custom ... + var totalEntities = await WebinarRepository.GetAllAsync(); + var items = await totalEntities.SelectAsync(async e => await ToXWebinarDto(e)); + 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.ApplySorting( + query.SortBy, + query.IsAscending + ); + + // + // Apply Paging ... + items = items.ApplyPaging( + query.Page, + query.PageSize + ); + } + + // + // Prepare Result ... + var result = new XQueryResult + { + Page = query.Page, + PageSize = query.PageSize, + TotalPages = totalPagesCount, + TotalItems = totalItemsCount, + TotalFilteredPages = filteredPagesCount, + TotalFilteredItems = filteredItemsCount + }; + + // + return result; + } + + /// + /// Remove Specified Webinar ... + /// + /// + /// + /// + public async Task RemoveWebinar( + Guid webinarId, + string requesterUserSelectByParam = null + ) + { + // + bool result = false; + + // + // Validate ... + result = + !webinarId.IsNull() && + !webinarId.IsDefaultGuid(); + if (!result) + { + return result; + } + + // + // Get Entity ... + var entity = await WebinarRepository.GetAsync(webinarId); + result = !entity.IsNullOrDefault(); + if (!result) + { + return result; + } + + // + // Check Permissions ... + result = entity.OwnerId == requesterUserSelectByParam; + if (!result) + { + return result; + } + + // + // Handle Remove ... + var subscribers = await GetSubscribers( + webinarId: webinarId, + requesterUserSelectByParam: requesterUserSelectByParam + ); + try + { + // + await subscribers.SelectAsync(async s => await Unsubscribe( + webinarId: webinarId, + subscriberId: s.SubscriberId, + requesterUserSelectByParam: requesterUserSelectByParam + )); + + // + string resourceID = webinarId.ToString(); + await TitleResourceProvider.Remove(resourceID); + await DescriptionResourceProvider.Remove(resourceID); + + // + await WebinarRepository.RemoveAsync(webinarId); + } + catch { } + + // + // Check Entity Removed ... + result = !await WebinarRepository.IsExistsAsync(webinarId); + + // + return result; + } + #endregion + + // + #region WebinarSubscriber ... + /// + /// Subscribe a User to Webinar ... + /// + /// + /// + /// + /// + public async Task Subscribe( + Guid webinarId, + string subscriberId, + string requesterUserSelectByParam = null + ) + { + // + var result = false; + + // + // Validate ... + result = + !webinarId.IsNull() && + !webinarId.IsDefaultGuid() && + !subscriberId.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + // Check Webinar Exists ... + result = await WebinarRepository.IsExistsAsync(webinarId); + if (!result) + { + return result; + } + + // + // Retrieve Webinar Entity ... + var webinar = await WebinarRepository.GetAsync(webinarId); + + // + // Check Webinar Enabled ... + result = + webinar.Enabled || + requesterUserSelectByParam == webinar.OwnerId; + if (!result) + { + return result; + } + + // + // Check Webinar Type ... + result = + webinar.Type == XWebinarType.Public || + requesterUserSelectByParam == webinar.OwnerId; + if (!result) + { + return result; + } + + // + // Check User Exists ... + var device = await IdentityProvider.GetDevice(); + XOpenActionUserInfoDto userInfo = null; + try + { + // + userInfo = await IdentityProvider.GetUserInfo( + device: device, + userSelectByParam: subscriberId + ); + } + catch { } + result = !userInfo.IsNullOrDefault(); + if (!result) + { + return result; + } + + // + // Create Subscription Model ... + var entity = new XWebinarSubscriber + { + WebinarId = webinarId, + SubscriberId = subscriberId, + SubscribedOn = DateTime.UtcNow, + }; + entity = await WebinarSubscriberRepository.AddAsync(entity); + result = !entity.IsNullOrDefault(); + + // + return result; + } + + /// + /// Check Specified User Subscribed on a Webinar or not ... + /// + /// + /// + /// + public async Task IsSubscribed( + Guid webinarId, + string subscriberId + ) + { + // + bool result = false; + + // + // Validate ... + result = + !webinarId.IsNull() && + !webinarId.IsDefaultGuid() && + !subscriberId.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + // Check Webinar Exists ... + result = await WebinarRepository.IsExistsAsync(webinarId); + if (!result) + { + return result; + } + + // + // Check User Exists ... + var device = await IdentityProvider.GetDevice(); + XOpenActionUserInfoDto userInfo = null; + try + { + // + userInfo = await IdentityProvider.GetUserInfo( + device: device, + userSelectByParam: subscriberId + ); + } + catch { } + result = !userInfo.IsNullOrDefault(); + if (!result) + { + return result; + } + + // + result = !(await WebinarSubscriberRepository.FindOneAsync(ws => + ws.WebinarId == webinarId && + ws.SubscriberId == subscriberId + )).IsNull(); + + // + return result; + } + + /// + /// Unsubscribe Specified User from a Specified Webinar ... + /// + /// + /// + /// + /// + public async Task Unsubscribe( + Guid webinarId, + string subscriberId, + string requesterUserSelectByParam = null + ) + { + // + var result = false; + + // + // Validate ... + result = + !webinarId.IsNull() && + !webinarId.IsDefaultGuid() && + !subscriberId.IsNullOrEmpty(); + if (!result) + { + return result; + } + + // + // Check Subscription .. + result = await IsSubscribed( + webinarId: webinarId, + subscriberId: subscriberId + ); + if (!result) + { + return result; + } + + // + // Retrieve Webinar ... + var webinar = await WebinarRepository.GetAsync(webinarId); + + // + // Check Permission ... + result = + requesterUserSelectByParam == subscriberId || + requesterUserSelectByParam == webinar.OwnerId; + if (!result) + { + return result; + } + + // + // Retrieve Subscription Entity ... + var entity = await WebinarSubscriberRepository.FindOneAsync(ws => + ws.WebinarId == webinarId && + ws.SubscriberId == subscriberId + ); + entity = await WebinarSubscriberRepository.RemoveAsync(entity); + result = !entity.IsNullOrDefault(); + + // + return result; + } + + /// + /// Retrieve Specified Subscriber ... + /// + /// + /// + /// + /// + public async Task GetSubscriber( + Guid webinarId, + string subscriberId, + string requesterUserSelectByParam = null + ) + { + // + // Validate ... + bool isValid = + !webinarId.IsNull() && + !webinarId.IsDefaultGuid() && + !subscriberId.IsNullOrEmpty(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Webinar and Validate it ... + var webinar = await WebinarRepository.GetAsync(webinarId); + isValid = !webinar.IsNullOrDefault(); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Retrieve Entity and Validate it ... + var entity = await WebinarSubscriberRepository.FindOneAsync(e => + e.WebinarId == webinarId && + e.SubscriberId == subscriberId + ); + isValid = !entity.IsNullOrDefault(); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Check Permissions ... + isValid = + subscriberId == requesterUserSelectByParam || + webinar.OwnerId == requesterUserSelectByParam; + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + // Converts to Subscriber Dto ... + var result = await ToXWebinarSubscriberDto(entity); + return result; + } + + /// + /// Get all Specified Webinars Subscribers ... + /// + /// + /// + /// + public async Task> GetSubscribers( + Guid webinarId, + string requesterUserSelectByParam = null + ) + { + // + // Validate ... + bool isValid = + !webinarId.IsNull() && + !webinarId.IsDefaultGuid(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve and Validate Webinar Entity ... + var webinar = await WebinarRepository.GetAsync(webinarId); + isValid = !webinar.IsNullOrDefault(); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Check Permissions ... + isValid = + webinar.OwnerId == requesterUserSelectByParam; + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + var subscribers = await WebinarSubscriberRepository.FindManyAsync(e => + e.WebinarId == webinarId + ); + + // + // Prepare Result ... + var result = await subscribers + .SelectAsync(async s => await ToXWebinarSubscriberDto(s)); + + // + return result; + } + + /// + /// Query Specified Webinar's Subscribers ... + /// + /// + /// + /// + /// + public async Task> QuerySubscribers( + Guid webinarId, + XQuery query, + string requesterUserSelectByParam = null + ) + { + // + // Validate ... + bool isValid = + !query.IsNull() && + !webinarId.IsNull() && + !webinarId.IsDefaultGuid(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + query = query.NormalizeQuery(DataSercviceConfiguration); + + // + // Check Webinar Exists ... + isValid = await WebinarRepository.IsExistsAsync(webinarId); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Retrieve all Items ... + var items = await GetSubscribers( + webinarId: webinarId, + requesterUserSelectByParam: requesterUserSelectByParam + ); + var totalItemsCount = items.Count(); + + // + // Try Filter Items ... + if (!query.Filter.IsNullOrEmpty()) + { + // + var filteredItems = new List(); + + // + // Implement Filtering Senario ... + + // + items = filteredItems.AsEnumerable(); + } + var filteredItemsCount = items.Count(); + + // + // Counting Pages ... + var totalPagesCount = query.CountPages(totalItemsCount); + var filteredPagesCount = query.CountPages(filteredItemsCount); + + // + // Apply Sorting and Paging ... + if (totalItemsCount > 0 && + filteredItemsCount > 0) + { + // + // Apply Sorting ... + items = items.ApplySorting( + query.SortBy, + query.IsAscending + ); + + // + // Apply Paging ... + items = items.ApplyPaging( + query.Page, + query.PageSize + ); + } + + // + // Prepare Result ... + var result = new XQueryResult + { + Page = query.Page, + PageSize = query.PageSize, + TotalPages = totalPagesCount, + TotalItems = totalItemsCount, + TotalFilteredPages = filteredPagesCount, + TotalFilteredItems = filteredItemsCount + }; // return result; diff --git a/Providers/XWebinarTitleResourceProvider.cs b/Providers/XWebinarTitleResourceProvider.cs new file mode 100644 index 0000000..a233a94 --- /dev/null +++ b/Providers/XWebinarTitleResourceProvider.cs @@ -0,0 +1,23 @@ +using xCommons.Configurations; +using xStringService.Base; +using xStringService.Interfaces; +using xWebinarService.Interfaces; + +namespace xWebinarService.Providers +{ + /// + /// a Servide to Provides Requirements for XWebinar Title Resources ... + /// + public class XWebinarTitleResourceProvider : XBaseStringResourceProvider, IXWebinarTitleResourceProvider + { + public XWebinarTitleResourceProvider( + IXStringProvider provider, + XAppConfiguration appConfiguration + ) : base( + "XWEWBT", + provider, + appConfiguration + ) + { } + } +} \ No newline at end of file diff --git a/xWebinarService.csproj b/xWebinarService.csproj index 57cd5be..454982c 100644 --- a/xWebinarService.csproj +++ b/xWebinarService.csproj @@ -24,6 +24,7 @@ +