diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 4b23f5b..0fcc9e2 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -1648,31 +1648,30 @@ namespace Marco.Pms.Services.Service employeeId, projectId, tenantId, loggedInEmployee.Id); // Query the database for relevant project-level permission mappings - var permissionMappings = await _context.ProjectLevelPermissionMappings - .Include(p => p.Employee) - .ThenInclude(e => e!.JobRole) - .Include(p => p.Project) - .Include(p => p.Permission) - .ThenInclude(fp => fp!.Feature) - .AsNoTracking() - .Where(p => p.EmployeeId == employeeId - && p.ProjectId == projectId - && p.TenantId == tenantId) - .ToListAsync(); - - if (permissionMappings == null || !permissionMappings.Any()) + var employeeTask = Task.Run(async () => { - _logger.LogWarning("No project-level permissions found for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}", - employeeId, projectId, tenantId); - return ApiResponse.ErrorResponse("Project-Level Permissions not found", "Project-Level Permissions not found in database", 404); - } + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.Employees.FirstOrDefaultAsync(e => e.Id == employeeId); + }); + var projectTask = Task.Run(async () => + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.Projects.FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId); + }); + var permissionIdsTask = Task.Run(async () => + { + return await GetPermissionIdsByProject(projectId, employeeId, tenantId); + }); - // Map the employee, project, and permissions. - var employee = _mapper.Map(permissionMappings.First().Employee); - var project = _mapper.Map(permissionMappings.First().Project); - var permissions = permissionMappings - .Select(p => _mapper.Map(p.Permission)) - .ToList(); + await Task.WhenAll(employeeTask, projectTask, permissionIdsTask); + + var employee = employeeTask.Result; + var project = projectTask.Result; + var permissionIds = permissionIdsTask.Result; + + var permissions = await _context.FeaturePermissions + .Include(fp => fp.Feature) + .Where(fp => permissionIds.Contains(fp.Id)).ToListAsync(); if (employee == null) { @@ -1685,12 +1684,26 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("Project not found", "Project not found in database", 404); } + if (permissions == null || !permissions.Any()) + { + _logger.LogWarning("No project-level permissions found for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}", + employeeId, projectId, tenantId); + return ApiResponse.ErrorResponse("Project-Level Permissions not found", "Project-Level Permissions not found in database", 404); + } + + // Map the employee, project, and permissions. + var employeeVm = _mapper.Map(employee); + var projectVm = _mapper.Map(project); + var permissionVms = permissions + .Select(p => _mapper.Map(p)) + .ToList(); + // Prepare the result object. var result = new { - Employee = employee, - Project = project, - Permissions = permissions + Employee = employeeVm, + Project = projectVm, + Permissions = permissionVms }; _logger.LogInfo("Project-level permissions fetched successfully for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}", @@ -1797,57 +1810,7 @@ namespace Marco.Pms.Services.Service } public async Task> GetAllPermissionFroProjectAsync(Guid projectId, Employee loggedInEmployee, Guid tenantId) { - using var scope = _serviceScopeFactory.CreateScope(); - var _rolesHelper = scope.ServiceProvider.GetRequiredService(); - // 1. Try fetching permissions from cache (fast-path lookup). - var featurePermissionIds = await _cache.GetPermissions(loggedInEmployee.Id); - - // If not found in cache, fallback to database (slower). - if (featurePermissionIds == null) - { - var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(loggedInEmployee.Id); - featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList(); - } - - // 2. Handle project-level permission overrides if a project is specified. - if (projectId != Guid.Empty) - { - // Fetch permissions explicitly assigned to this employee in the project. - var projectLevelPermissionIds = await _context.ProjectLevelPermissionMappings - .Where(pl => pl.ProjectId == projectId && pl.EmployeeId == loggedInEmployee.Id) - .Select(pl => pl.PermissionId) - .ToListAsync(); - - if (projectLevelPermissionIds?.Any() ?? false) - { - - // Define modules where project-level overrides apply. - var projectLevelModuleIds = new HashSet - { - Guid.Parse("53176ebf-c75d-42e5-839f-4508ffac3def"), - Guid.Parse("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"), - Guid.Parse("81ab8a87-8ccd-4015-a917-0627cee6a100"), - Guid.Parse("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), - Guid.Parse("a8cf4331-8f04-4961-8360-a3f7c3cc7462") - }; - - // Get all feature permissions under those modules where the user didn't have explicit project-level grants. - var allOverriddenPermissions = await _context.FeaturePermissions - .Where(fp => projectLevelModuleIds.Contains(fp.FeatureId) && - !projectLevelPermissionIds.Contains(fp.Id)) - .Select(fp => fp.Id) - .ToListAsync(); - - // Apply overrides: - // - Remove global permissions overridden by project-level rules. - // - Add explicit project-level permissions. - featurePermissionIds = featurePermissionIds - .Except(allOverriddenPermissions) // Remove overridden - .Concat(projectLevelPermissionIds) // Add project-level - .Distinct() // Ensure no duplicates - .ToList(); - } - } + var featurePermissionIds = await GetPermissionIdsByProject(projectId, loggedInEmployee.Id, tenantId); return ApiResponse.SuccessResponse(featurePermissionIds, "Successfully featched the permission ids", 200); } #endregion @@ -1859,13 +1822,11 @@ namespace Marco.Pms.Services.Service List alloc = await _context.Projects.Where(c => c.TenantId == tanentId).ToListAsync(); return alloc; } - public async Task> GetProjectByEmployeeID(Guid employeeId) { List alloc = await _context.ProjectAllocations.Where(c => c.EmployeeId == employeeId && c.IsActive == true).Include(c => c.Project).ToListAsync(); return alloc; } - public async Task> GetTeamByProject(Guid TenantId, Guid ProjectId, bool IncludeInactive) { if (IncludeInactive) @@ -1882,7 +1843,6 @@ namespace Marco.Pms.Services.Service return employees; } } - public async Task> GetMyProjects(Guid tenantId, Employee LoggedInEmployee) { using var scope = _serviceScopeFactory.CreateScope(); @@ -1911,7 +1871,6 @@ namespace Marco.Pms.Services.Service } return projectIds; } - public async Task> GetMyProjectIdsAsync(Guid tenantId, Employee loggedInEmployee) { using var scope = _serviceScopeFactory.CreateScope(); @@ -1963,7 +1922,6 @@ namespace Marco.Pms.Services.Service return newProjectIds; } - /// /// Retrieves a list of ProjectInfoVMs by their IDs, using an efficient partial cache-hit strategy. /// It fetches what it can from the cache (as ProjectMongoDB), gets the rest from the @@ -2014,7 +1972,6 @@ namespace Marco.Pms.Services.Service return finalViewModels; } - private async Task GetProjectViewModel(Guid? id, Project project) { ProjectDetailsVM vm = new ProjectDetailsVM(); @@ -2164,7 +2121,6 @@ namespace Marco.Pms.Services.Service // Map from the database entity to the response ViewModel. return dbProject; } - private async Task UpdateCacheInBackground(Project project) { try @@ -2182,7 +2138,6 @@ namespace Marco.Pms.Services.Service _logger.LogError(ex, "Background cache update failed for project {ProjectId} ", project.Id); } } - private async Task UpdateCacheAndNotify(Dictionary workDelta, List affectedItems) { try @@ -2204,7 +2159,6 @@ namespace Marco.Pms.Services.Service _logger.LogError(ex, "An error occurred during background cache update/notification."); } } - private void ProcessBuilding(BuildingDto dto, Guid tenantId, InfraVM responseData, List messages, ISet projectIds, List cacheTasks) { Building building = _mapper.Map(dto); @@ -2227,7 +2181,6 @@ namespace Marco.Pms.Services.Service responseData.building = building; projectIds.Add(building.ProjectId); } - private void ProcessFloor(FloorDto dto, Guid tenantId, InfraVM responseData, List messages, ISet projectIds, List cacheTasks, IDictionary buildings) { Floor floor = _mapper.Map(dto); @@ -2253,7 +2206,6 @@ namespace Marco.Pms.Services.Service responseData.floor = floor; if (parentBuilding != null) projectIds.Add(parentBuilding.ProjectId); } - private void ProcessWorkArea(WorkAreaDto dto, Guid tenantId, InfraVM responseData, List messages, ISet projectIds, List cacheTasks, IDictionary floors) { WorkArea workArea = _mapper.Map(dto); @@ -2280,6 +2232,63 @@ namespace Marco.Pms.Services.Service responseData.workArea = workArea; if (parentBuilding != null) projectIds.Add(parentBuilding.ProjectId); } + private async Task> GetPermissionIdsByProject(Guid projectId, Guid EmployeeId, Guid tenantId) + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + using var scope = _serviceScopeFactory.CreateScope(); + + var _rolesHelper = scope.ServiceProvider.GetRequiredService(); + // 1. Try fetching permissions from cache (fast-path lookup). + var featurePermissionIds = await _cache.GetPermissions(EmployeeId); + + // If not found in cache, fallback to database (slower). + if (featurePermissionIds == null) + { + var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(EmployeeId); + featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList(); + } + + // 2. Handle project-level permission overrides if a project is specified. + if (projectId != Guid.Empty) + { + // Fetch permissions explicitly assigned to this employee in the project. + var projectLevelPermissionIds = await context.ProjectLevelPermissionMappings + .Where(pl => pl.ProjectId == projectId && pl.EmployeeId == EmployeeId && pl.TenantId == tenantId) + .Select(pl => pl.PermissionId) + .ToListAsync(); + + if (projectLevelPermissionIds?.Any() ?? false) + { + + // Define modules where project-level overrides apply. + var projectLevelModuleIds = new HashSet + { + Guid.Parse("53176ebf-c75d-42e5-839f-4508ffac3def"), + Guid.Parse("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"), + Guid.Parse("81ab8a87-8ccd-4015-a917-0627cee6a100"), + Guid.Parse("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"), + Guid.Parse("a8cf4331-8f04-4961-8360-a3f7c3cc7462") + }; + + // Get all feature permissions under those modules where the user didn't have explicit project-level grants. + var allOverriddenPermissions = await context.FeaturePermissions + .Where(fp => projectLevelModuleIds.Contains(fp.FeatureId) && + !projectLevelPermissionIds.Contains(fp.Id)) + .Select(fp => fp.Id) + .ToListAsync(); + + // Apply overrides: + // - Remove global permissions overridden by project-level rules. + // - Add explicit project-level permissions. + featurePermissionIds = featurePermissionIds + .Except(allOverriddenPermissions) // Remove overridden + .Concat(projectLevelPermissionIds) // Add project-level + .Distinct() // Ensure no duplicates + .ToList(); + } + } + return featurePermissionIds; + } #endregion }