// *********************************************************************** // // AgileWebs // // *********************************************************************** using Core.Blueprint.Mongo; using Core.Blueprint.Redis; using Core.Blueprint.Redis.Helpers; using Core.Thalos.BuildingBlocks; using Core.Thalos.Provider.Contracts; using Mapster; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver; using System.Text.RegularExpressions; namespace Core.Thalos.Provider.Providers.Onboarding { /// /// Handles all services and business rules related to . /// public class UserProvider : IUserProvider { private readonly CollectionRepository repository; private readonly ICacheSettings cacheSettings; private readonly IRedisCacheProvider cacheProvider; public UserProvider(CollectionRepository repository, IRedisCacheProvider cacheProvider, ICacheSettings cacheSettings ) { this.repository = repository; this.repository.CollectionInitialization(); this.cacheSettings = cacheSettings; this.cacheProvider = cacheProvider; } /// /// Creates a new User. /// /// The User to be created. /// A representing /// the asynchronous execution of the service. public async ValueTask CreateUser(Core.Thalos.Domain.Contexts.Onboarding.Request.UserRequest newUser, CancellationToken cancellationToken) { var userCollection = newUser.Adapt(); await repository.InsertOneAsync(userCollection); return userCollection; } /// /// Gets an User by identifier. /// /// The User mongo identifier. /// A representing /// the asynchronous execution of the service. public async ValueTask GetUserById(string _id, CancellationToken cancellationToken) { var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetUserById", _id); var cachedData = await cacheProvider.GetAsync(cacheKey); if (cachedData is not null) { return cachedData; } var user = await repository.FindByIdAsync(_id); await cacheProvider.SetAsync(cacheKey, user, TimeSpan.FromMinutes(cacheSettings.DefaultCacheDurationInMinutes)); return user; } /// /// Gets all the users. /// /// A representing /// the asynchronous execution of the service. public async ValueTask> GetAllUsers(CancellationToken cancellationToken) { var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetAllUsers"); var cachedData = await cacheProvider.GetAsync>(cacheKey) ?? []; if (cachedData.Any()) return cachedData; var users = await repository.AsQueryable(); await cacheProvider.SetAsync(cacheKey, users, TimeSpan.FromMinutes(cacheSettings.DefaultCacheDurationInMinutes)); return users; } /// /// Gets an User by email. /// /// The User email. /// A representing /// the asynchronous execution of the service. public async ValueTask GetUserByEmail(string email, CancellationToken cancellationToken) { var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetUserByEmail", email); var cachedData = await cacheProvider.GetAsync(cacheKey); //if (cachedData is not null) { return cachedData; } var user = await repository.FindOneAsync( u => u.Email == email && u.Status == Core.Blueprint.Mongo.StatusEnum.Active); await cacheProvider.SetAsync(cacheKey, user); return user; } /// /// Validates if a users exists by email.. /// /// The User email. /// A representing /// the asynchronous execution of the service. public async ValueTask ValidateUserExistence(string email, CancellationToken cancellationToken) { var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetUserByEmail", email); var cachedData = await cacheProvider.GetAsync(cacheKey); //if (cachedData is not null) { return cachedData; } var user = await repository.FindOneAsync( u => u.Email == email && u.Status == Core.Blueprint.Mongo.StatusEnum.Active); UserExistenceAdapter userExistance = new UserExistenceAdapter(); userExistance.Existence = (user != null) ? true : false; await cacheProvider.SetAsync(cacheKey, userExistance); return userExistance; } /// /// Changes the status of the user. /// /// The user mongo identifier. /// The new status of the user. /// A representing /// the asynchronous execution of the service. public async ValueTask ChangeUserStatus(string _id, Core.Blueprint.Mongo.StatusEnum newStatus, CancellationToken cancellationToken) { var entity = await repository.FindByIdAsync(_id); if (entity is not null) { entity.Status = newStatus; return repository.ReplaceOneAsync(entity).Result; } else return null; } /// /// Updates a User by _id. /// /// The User to be updated. /// The User mongo identifier. /// A representing /// the asynchronous execution of the service. public async ValueTask UpdateUser(UserAdapter entity, CancellationToken cancellationToken) { var updatedEntity = await repository.ReplaceOneAsync(entity); return updatedEntity; } /// /// Logs in the user. /// /// The User mongo identifier. /// A representing /// the asynchronous execution of the service. public async ValueTask LogInUser(string email, CancellationToken cancellationToken) { var user = await repository.FindOneAsync( u => u.Email == email && u.Status == Core.Blueprint.Mongo.StatusEnum.Active); user.LastLogIn = DateTime.UtcNow; await repository.ReplaceOneAsync(user); return user; } /// /// Logs out the user's session. /// /// The User email. /// A representing /// the asynchronous execution of the service. public async ValueTask LogOutUserSession(string email, CancellationToken cancellationToken) { var user = await repository.FindOneAsync( u => u.Email == email && u.Status == Core.Blueprint.Mongo.StatusEnum.Active); user.LastLogOut = DateTime.UtcNow; await repository.ReplaceOneAsync(user); return user; } /// /// Gets the token adapter for a user. /// /// The user's email. /// A representing the asynchronous execution of the service. public async ValueTask GetToken(string email, CancellationToken cancellationToken) { try { var pipeline = new[] { new BsonDocument("$match", new BsonDocument { { "email", new BsonDocument { { "$regex", $"^{Regex.Escape(email)}$" }, { "$options", "i" } } }, { "status", Core.Blueprint.Mongo.StatusEnum.Active.ToString() } }), new BsonDocument("$lookup", new BsonDocument { { "from", "Roles" }, { "localField", "roleId" }, { "foreignField", "_id" }, { "as", "role" } }), new BsonDocument("$unwind", "$role"), new BsonDocument("$match", new BsonDocument("role.status", Core.Blueprint.Mongo.StatusEnum.Active.ToString())), new BsonDocument("$lookup", new BsonDocument { { "from", "Tenants" }, { "localField", "tenantId" }, { "foreignField", "_id" }, { "as", "tenant" } }), new BsonDocument("$unwind", "$tenant"), new BsonDocument("$addFields", new BsonDocument { { "role.permissions", new BsonDocument("$map", new BsonDocument { { "input", "$role.permissions" }, { "as", "perm" }, { "in", new BsonDocument("$toObjectId", "$$perm") } }) }, { "role.modules", new BsonDocument("$map", new BsonDocument { { "input", "$role.modules" }, { "as", "mod" }, { "in", new BsonDocument("$toObjectId", "$$mod") } }) } }), new BsonDocument("$lookup", new BsonDocument { { "from", "Permissions" }, { "localField", "role.permissions" }, { "foreignField", "_id" }, { "as", "permissions" } }), new BsonDocument("$lookup", new BsonDocument { { "from", "Modules" }, { "localField", "role.modules" }, { "foreignField", "_id" }, { "as", "modules" } }), new BsonDocument("$project", new BsonDocument { { "_id", 1 }, { "guid", 1 }, { "email", 1 }, { "name", 1 }, { "middleName", 1 }, { "lastName", 1 }, { "displayName", 1 }, { "roleId", 1 }, { "companies", 1 }, { "projects", 1 }, { "lastLogIn", 1 }, { "lastLogOut", 1 }, { "createdBy", 1 }, { "updatedBy", 1 }, { "status", 1 }, { "createdAt", 1 }, { "updatedAt", 1 }, { "role._id", 1 }, { "role.name", 1 }, { "role.description", 1 }, { "role.applications", 1 }, { "role.permissions", 1 }, { "role.modules", 1 }, { "role.status", 1 }, { "role.createdAt", 1 }, { "role.updatedAt", 1 }, { "role.createdBy", 1 }, { "role.updatedBy", 1 }, { "tenant._id", 1 }, { "tenant.name", 1 }, { "tenant.taxIdentifier", 1 }, { "tenant.addressLine1", 1 }, { "tenant.addressLine2", 1 }, { "tenant.city", 1 }, { "tenant.state", 1 }, { "tenant.country", 1 }, { "tenant.postalCode", 1 }, { "tenant.contactEmail", 1 }, { "tenant.contactPhone", 1 }, { "tenant.website", 1 }, { "tenant.connectionString", 1 }, { "tenant.isolated", 1 }, { "tenant.status", 1 }, { "tenant.createdAt", 1 }, { "tenant.updatedAt", 1 }, { "tenant.createdBy", 1 }, { "tenant.updatedBy", 1 }, { "permissions", 1 }, { "modules", 1 } }) }; var result = await repository.FindOnePipelineAsync(pipeline); if (result is null) return null; DateTime SafeToUtc(BsonValue value) { if (value == null || value.IsBsonNull) return DateTime.MinValue; return value.IsBsonDateTime ? value.ToUniversalTime() : DateTime.MinValue; } var tokenAdapter = new TokenAdapter { User = new UserAdapter { _Id = result["_id"]?.ToString() ?? "", Email = result.Contains("email") && !result["email"].IsBsonNull ? result["email"].AsString : string.Empty, Name = result.Contains("name") && !result["name"].IsBsonNull ? result["name"].AsString : string.Empty, MiddleName = result.Contains("middleName") && !result["middleName"].IsBsonNull ? result["middleName"].AsString : string.Empty, LastName = result.Contains("lastName") && !result["lastName"].IsBsonNull ? result["lastName"].AsString : string.Empty, DisplayName = result.Contains("displayName") && !result["displayName"].IsBsonNull ? result["displayName"].AsString : string.Empty, RoleId = result.Contains("roleId") && !result["roleId"].IsBsonNull ? result["roleId"].ToString() : string.Empty, LastLogIn = SafeToUtc(result.Contains("lastLogIn") ? result["lastLogIn"] : null), LastLogOut = SafeToUtc(result.Contains("lastLogOut") ? result["lastLogOut"] : null), CreatedAt = SafeToUtc(result.Contains("createdAt") ? result["createdAt"] : null), UpdatedAt = SafeToUtc(result.Contains("updatedAt") ? result["updatedAt"] : null), CreatedBy = result.Contains("createdBy") && !result["createdBy"].IsBsonNull ? result["createdBy"].AsString : string.Empty, UpdatedBy = result.Contains("updatedBy") && !result["updatedBy"].IsBsonNull ? result["updatedBy"].AsString : string.Empty, Status = result.Contains("status") && !result["status"].IsBsonNull ? (Core.Blueprint.Mongo.StatusEnum)Enum.Parse(typeof(Core.Blueprint.Mongo.StatusEnum), result["status"].AsString) : Core.Blueprint.Mongo.StatusEnum.Inactive }, Role = new RoleAdapter { Id = result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("_id") ? result["role"]["_id"]?.ToString() ?? "" : string.Empty, Name = result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("name") ? result["role"]["name"]?.AsString ?? "" : string.Empty, Description = result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("description") ? result["role"]["description"]?.AsString ?? "" : string.Empty, Applications = result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("applications") && result["role"]["applications"].IsBsonArray ? result["role"]["applications"].AsBsonArray .Where(app => app != null && app.IsInt32) .Select(app => (ApplicationsEnum)app.AsInt32) .ToArray() : Array.Empty(), Modules = result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("modules") && result["role"]["modules"].IsBsonArray ? result["role"]["modules"].AsBsonArray .Where(m => m != null) .Select(m => m.ToString() ?? "") .ToArray() : Array.Empty(), Permissions = result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("permissions") && result["role"]["permissions"].IsBsonArray ? result["role"]["permissions"].AsBsonArray .Where(p => p != null) .Select(p => p.ToString() ?? "") .ToArray() : Array.Empty(), Status = result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("status") && !result["role"]["status"].IsBsonNull ? (Core.Blueprint.Mongo.StatusEnum)Enum.Parse(typeof(Core.Blueprint.Mongo.StatusEnum), result["role"]["status"].AsString) : Core.Blueprint.Mongo.StatusEnum.Inactive, CreatedAt = SafeToUtc(result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("createdAt") ? result["role"]["createdAt"] : null), UpdatedAt = SafeToUtc(result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("updatedAt") ? result["role"]["updatedAt"] : null), CreatedBy = result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("createdBy") && !result["role"]["createdBy"].IsBsonNull ? result["role"]["createdBy"].AsString : string.Empty, UpdatedBy = result.Contains("role") && result["role"].IsBsonDocument && result["role"].AsBsonDocument.Contains("updatedBy") && !result["role"]["updatedBy"].IsBsonNull ? result["role"]["updatedBy"].AsString : string.Empty }, Tenant = result.Contains("tenant") && result["tenant"].IsBsonDocument ? new TenantAdapter { Id = result.Contains("tenant") && result["tenant"].IsBsonDocument && result["tenant"].AsBsonDocument.Contains("_id") ? result["tenant"]["_id"]?.ToString() ?? "" : string.Empty, Name = result["tenant"].AsBsonDocument.Contains("name") && !result["tenant"]["name"].IsBsonNull ? result["tenant"]["name"].AsString : string.Empty, TaxIdentifier = result["tenant"].AsBsonDocument.Contains("taxIdentifier") && !result["tenant"]["taxIdentifier"].IsBsonNull ? result["tenant"]["taxIdentifier"].AsString : string.Empty, AddressLine1 = result["tenant"].AsBsonDocument.Contains("addressLine1") && !result["tenant"]["addressLine1"].IsBsonNull ? result["tenant"]["addressLine1"].AsString : string.Empty, AddressLine2 = result["tenant"].AsBsonDocument.Contains("addressLine2") && !result["tenant"]["addressLine2"].IsBsonNull ? result["tenant"]["addressLine2"].AsString : null, City = result["tenant"].AsBsonDocument.Contains("city") && !result["tenant"]["city"].IsBsonNull ? result["tenant"]["city"].AsString : string.Empty, State = result["tenant"].AsBsonDocument.Contains("state") && !result["tenant"]["state"].IsBsonNull ? result["tenant"]["state"].AsString : string.Empty, Country = result["tenant"].AsBsonDocument.Contains("country") && !result["tenant"]["country"].IsBsonNull ? result["tenant"]["country"].AsString : string.Empty, PostalCode = result["tenant"].AsBsonDocument.Contains("postalCode") && !result["tenant"]["postalCode"].IsBsonNull ? result["tenant"]["postalCode"].AsString : string.Empty, ContactEmail = result["tenant"].AsBsonDocument.Contains("contactEmail") && !result["tenant"]["contactEmail"].IsBsonNull ? result["tenant"]["contactEmail"].AsString : string.Empty, ContactPhone = result["tenant"].AsBsonDocument.Contains("contactPhone") && !result["tenant"]["contactPhone"].IsBsonNull ? result["tenant"]["contactPhone"].AsString : string.Empty, Website = result["tenant"].AsBsonDocument.Contains("website") && !result["tenant"]["website"].IsBsonNull ? result["tenant"]["website"].AsString : null, ConnectionString = result["tenant"].AsBsonDocument.Contains("connectionString") && !result["tenant"]["connectionString"].IsBsonNull ? result["tenant"]["connectionString"].AsString : null, Isolated = result["tenant"].AsBsonDocument.Contains("isolated") && !result["tenant"]["isolated"].IsBsonNull ? result["tenant"]["isolated"].ToBoolean() : false, CreatedAt = SafeToUtc(result.Contains("tenant") && result["tenant"].IsBsonDocument && result["tenant"].AsBsonDocument.Contains("createdAt") ? result["tenant"]["createdAt"] : null), UpdatedAt = SafeToUtc(result.Contains("tenant") && result["tenant"].IsBsonDocument && result["tenant"].AsBsonDocument.Contains("updatedAt") ? result["tenant"]["updatedAt"] : null), CreatedBy = result["tenant"].AsBsonDocument.Contains("createdBy") && !result["tenant"]["createdBy"].IsBsonNull ? result["tenant"]["createdBy"].AsString : string.Empty, UpdatedBy = result["tenant"].AsBsonDocument.Contains("updatedBy") && !result["tenant"]["updatedBy"].IsBsonNull ? result["tenant"]["updatedBy"].AsString : string.Empty, Status = result["tenant"].AsBsonDocument.Contains("status") && !result["tenant"]["status"].IsBsonNull ? (Core.Blueprint.Mongo.StatusEnum)Enum.Parse(typeof(Core.Blueprint.Mongo.StatusEnum), result["tenant"]["status"].AsString) : Core.Blueprint.Mongo.StatusEnum.Active } : null, Permissions = result.Contains("permissions") && result["permissions"].IsBsonArray ? result["permissions"].AsBsonArray .Where(p => p != null && p.IsBsonDocument) .Select(p => BsonSerializer.Deserialize(p.AsBsonDocument)) .Where(p => p.Status == Core.Blueprint.Mongo.StatusEnum.Active) .ToList() : new List(), Modules = result.Contains("modules") && result["modules"].IsBsonArray ? result["modules"].AsBsonArray .Where(p => p != null && p.IsBsonDocument) .Select(p => BsonSerializer.Deserialize(p.AsBsonDocument)) .Where(p => p.Status == Core.Blueprint.Mongo.StatusEnum.Active) .ToList() : new List() }; return tokenAdapter; } catch (Exception ex) { throw new Exception(ex.Message, ex); } } /// /// Deletes an User by _id. /// /// The User mongo identifier. /// A representing /// the asynchronous execution of the service. public async ValueTask DeleteUser(string _id, CancellationToken cancellationToken) { var entity = await repository.DeleteOneAsync(doc => doc.Id == _id); return entity; } } }