Added updatelogs in update contact API
This commit is contained in:
parent
759d152a9a
commit
6688b76145
@ -25,8 +25,30 @@ namespace Marco.Pms.Helpers.Utility
|
||||
#region =================================================================== Update Log Helper Functions ===================================================================
|
||||
public async Task PushToUpdateLogsAsync(UpdateLogsObject oldObject, string collectionName)
|
||||
{
|
||||
var collection = _mongoDatabase.GetCollection<UpdateLogsObject>(collectionName);
|
||||
await collection.InsertOneAsync(oldObject);
|
||||
try
|
||||
{
|
||||
var collection = _mongoDatabase.GetCollection<UpdateLogsObject>(collectionName);
|
||||
await collection.InsertOneAsync(oldObject);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occured while saving object of update logs in collection: {Collection}", collectionName);
|
||||
}
|
||||
}
|
||||
public async Task PushListToUpdateLogsAsync(List<UpdateLogsObject> oldObjects, string collectionName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var collection = _mongoDatabase.GetCollection<UpdateLogsObject>(collectionName);
|
||||
if (oldObjects.Any())
|
||||
{
|
||||
await collection.InsertManyAsync(oldObjects);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception occured while saving list of update logs in collection: {Collection}", collectionName);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<UpdateLogsObject>> GetFromUpdateLogsByEntityIdAsync(Guid entityId, string collectionName)
|
||||
|
@ -80,6 +80,13 @@ namespace Marco.Pms.Services.Controllers
|
||||
var response = await _directoryService.GetOrganizationListAsync(tenantId, loggedInEmployee);
|
||||
return Ok(response);
|
||||
}
|
||||
[HttpGet("designations")]
|
||||
public async Task<IActionResult> GetDesignationList()
|
||||
{
|
||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var response = await _directoryService.GetDesignationListAsync(tenantId, loggedInEmployee);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -251,9 +251,11 @@ namespace Marco.Pms.Services.MappingProfiles
|
||||
|
||||
CreateMap<ContactPhone, ContactPhoneVM>();
|
||||
CreateMap<CreateContactPhoneDto, ContactPhone>();
|
||||
CreateMap<UpdateContactPhoneDto, ContactPhone>();
|
||||
|
||||
CreateMap<ContactEmail, ContactEmailVM>();
|
||||
CreateMap<CreateContactEmailDto, ContactEmail>();
|
||||
CreateMap<UpdateContactEmailDto, ContactEmail>();
|
||||
|
||||
CreateMap<ContactCategoryMaster, ContactCategoryVM>();
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
using AutoMapper;
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Helpers.Utility;
|
||||
using Marco.Pms.Model.Directory;
|
||||
using Marco.Pms.Model.Dtos.Directory;
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Mapper;
|
||||
using Marco.Pms.Model.MongoDBModels.Utility;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Directory;
|
||||
using Marco.Pms.Model.ViewModels.Master;
|
||||
@ -26,7 +28,13 @@ namespace Marco.Pms.Services.Service
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly PermissionServices _permissionServices;
|
||||
private readonly UtilityMongoDBHelper _updateLogsHelper;
|
||||
|
||||
|
||||
private static readonly string contactCollection = "ContactModificationLog";
|
||||
private static readonly string contactPhoneCollection = "ContactPhoneModificationLog";
|
||||
private static readonly string contactEmailCollection = "ContactEmailModificationLog";
|
||||
|
||||
|
||||
public DirectoryService(
|
||||
IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||
@ -34,8 +42,7 @@ namespace Marco.Pms.Services.Service
|
||||
ILoggingService logger,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
UserHelper userHelper,
|
||||
IMapper mapper,
|
||||
PermissionServices permissionServices)
|
||||
IMapper mapper)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
@ -43,7 +50,8 @@ namespace Marco.Pms.Services.Service
|
||||
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
|
||||
_userHelper = userHelper ?? throw new ArgumentNullException(nameof(userHelper));
|
||||
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
||||
_permissionServices = permissionServices ?? throw new ArgumentNullException(nameof(permissionServices));
|
||||
using var scope = serviceScopeFactory.CreateScope();
|
||||
_updateLogsHelper = scope.ServiceProvider.GetRequiredService<UtilityMongoDBHelper>();
|
||||
}
|
||||
#region =================================================================== Contact APIs ===================================================================
|
||||
|
||||
@ -747,6 +755,66 @@ namespace Marco.Pms.Services.Service
|
||||
return ApiResponse<object>.ErrorResponse("An internal server error occurred.", ExceptionMapper(ex), 500);
|
||||
}
|
||||
}
|
||||
public async Task<ApiResponse<object>> GetDesignationListAsync(Guid tenantId, Employee loggedInEmployee)
|
||||
{
|
||||
// --- Parameter Validation ---
|
||||
// Fail fast if essential parameters are not provided.
|
||||
ArgumentNullException.ThrowIfNull(loggedInEmployee);
|
||||
|
||||
var employeeId = loggedInEmployee.Id;
|
||||
|
||||
try
|
||||
{
|
||||
// --- 1. Permission Check ---
|
||||
// Verify that the employee has at least one of the required permissions to view this data.
|
||||
// This prevents unauthorized data access early in the process.
|
||||
var (hasAdminPermission, hasManagerPermission, hasUserPermission) = await CheckPermissionsAsync(employeeId);
|
||||
|
||||
if (!hasAdminPermission && !hasManagerPermission && !hasUserPermission)
|
||||
{
|
||||
// Log the specific denial reason for security auditing.
|
||||
_logger.LogWarning("Access DENIED for employee {EmployeeId} attempting to get designations list for tenant {TenantId} due to lack of permissions.", employeeId, tenantId);
|
||||
// Return a strongly-typed error response.
|
||||
return ApiResponse<object>.ErrorResponse("Access Denied", "You do not have permission to perform this action.", 403);
|
||||
}
|
||||
|
||||
// --- 2. Database Query ---
|
||||
// Build and execute the database query efficiently.
|
||||
_logger.LogDebug("Fetching designations list for Tenant {TenantId} by Employee {EmployeeId}", tenantId, employeeId);
|
||||
|
||||
var designationsList = await _context.Contacts
|
||||
// Filter contacts by the specified tenant to ensure data isolation and Filter out contacts that do not have an designations name to ensure data quality.
|
||||
.Where(c => c.TenantId == tenantId && !string.IsNullOrEmpty(c.Designation))
|
||||
|
||||
// Project only the 'Designation' column. This is a major performance optimization
|
||||
// as it avoids loading entire 'Contact' entities into memory.
|
||||
.Select(c => c.Designation)
|
||||
// Let the database perform the distinct operation, which is highly efficient.
|
||||
.Distinct()
|
||||
// Execute the query asynchronously and materialize the results into a list.
|
||||
.ToListAsync();
|
||||
|
||||
// --- 3. Success Response ---
|
||||
// Log the successful operation with key details.
|
||||
_logger.LogInfo("Successfully fetched {DesignationsCount} distinct designations for Tenant {TenantId} for employee {EmployeeId}", designationsList.Count, tenantId, employeeId);
|
||||
|
||||
// Return a strongly-typed success response with the data and a descriptive message.
|
||||
return ApiResponse<object>.SuccessResponse(
|
||||
designationsList,
|
||||
$"{designationsList.Count} unique designation(s) found.",
|
||||
200
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// --- 4. Exception Handling ---
|
||||
// Log the full exception details for effective debugging, including context.
|
||||
_logger.LogError(ex, "An unexpected error occurred while fetching designation list for Tenant {TenantId} by Employee {EmployeeId}", tenantId, employeeId);
|
||||
|
||||
// Return a generic, strongly-typed error response to the client to avoid leaking implementation details.
|
||||
return ApiResponse<object>.ErrorResponse("An internal server error occurred.", ExceptionMapper(ex), 500);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -968,6 +1036,19 @@ namespace Marco.Pms.Services.Service
|
||||
var allTags = await context.ContactTagMasters.Where(t => t.TenantId == tenantId).ToListAsync();
|
||||
var tagNameLookup = allTags.ToDictionary(t => t.Name.ToLowerInvariant(), t => t);
|
||||
|
||||
var contactObject = _updateLogsHelper.EntityToBsonDocument(contact);
|
||||
|
||||
var contactUpdateLog = new UpdateLogsObject
|
||||
{
|
||||
EntityId = contact.Id.ToString(),
|
||||
UpdatedById = loggedInEmployee.Id.ToString(),
|
||||
OldObject = contactObject,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
List<UpdateLogsObject> phoneUpdateLogs = new List<UpdateLogsObject>();
|
||||
List<UpdateLogsObject> emailUpdateLogs = new List<UpdateLogsObject>();
|
||||
|
||||
// ---------------------- Update Phones -----------------------
|
||||
if (updateContact.ContactPhones != null)
|
||||
{
|
||||
@ -975,11 +1056,26 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
foreach (var phoneDto in updateContact.ContactPhones)
|
||||
{
|
||||
var phoneEntity = phoneDto.ToContactPhoneFromUpdateContactPhoneDto(tenantId, contact.Id);
|
||||
if (phoneDto.Id != null && phoneDto.Id != Guid.Empty && phoneIds.Contains(phoneEntity.Id))
|
||||
var phoneEntity = _mapper.Map<ContactPhone>(phoneDto);
|
||||
phoneEntity.TenantId = tenantId;
|
||||
phoneEntity.ContactId = contact.Id;
|
||||
var existingPhone = phones.FirstOrDefault(p => p.Id == phoneEntity.Id);
|
||||
if (phoneDto.Id != null && phoneDto.Id != Guid.Empty && existingPhone != null)
|
||||
{
|
||||
var phoneObject = _updateLogsHelper.EntityToBsonDocument(existingPhone);
|
||||
phoneUpdateLogs.Add(new UpdateLogsObject
|
||||
{
|
||||
EntityId = existingPhone.Id.ToString(),
|
||||
UpdatedById = loggedInEmployee.Id.ToString(),
|
||||
OldObject = phoneObject,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
});
|
||||
context.ContactsPhones.Update(phoneEntity);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.ContactsPhones.Add(phoneEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove phones not updated in payload
|
||||
@ -987,6 +1083,14 @@ namespace Marco.Pms.Services.Service
|
||||
{
|
||||
if (!updatedPhoneIds.Contains(phone.Id))
|
||||
{
|
||||
var phoneObject = _updateLogsHelper.EntityToBsonDocument(phone);
|
||||
phoneUpdateLogs.Add(new UpdateLogsObject
|
||||
{
|
||||
EntityId = phone.Id.ToString(),
|
||||
UpdatedById = loggedInEmployee.Id.ToString(),
|
||||
OldObject = phoneObject,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
});
|
||||
context.ContactsPhones.Remove(phone);
|
||||
}
|
||||
}
|
||||
@ -1003,11 +1107,29 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
foreach (var emailDto in updateContact.ContactEmails)
|
||||
{
|
||||
var emailEntity = emailDto.ToContactEmailFromUpdateContactEmailDto(tenantId, contact.Id);
|
||||
if (emailDto.Id != null && emailDto.Id != Guid.Empty && emailIds.Contains(emailEntity.Id))
|
||||
var emailEntity = _mapper.Map<ContactEmail>(emailDto);
|
||||
emailEntity.TenantId = tenantId;
|
||||
emailEntity.ContactId = contact.Id;
|
||||
|
||||
var existingEmail = emails.FirstOrDefault(e => e.Id == emailEntity.Id);
|
||||
|
||||
if (emailDto.Id != null && emailDto.Id != Guid.Empty && existingEmail != null)
|
||||
{
|
||||
var emailObject = _updateLogsHelper.EntityToBsonDocument(existingEmail);
|
||||
emailUpdateLogs.Add(new UpdateLogsObject
|
||||
{
|
||||
EntityId = existingEmail.Id.ToString(),
|
||||
UpdatedById = loggedInEmployee.Id.ToString(),
|
||||
OldObject = emailObject,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
context.ContactsEmails.Update(emailEntity);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.ContactsEmails.Add(emailEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove emails not updated in payload
|
||||
@ -1015,6 +1137,14 @@ namespace Marco.Pms.Services.Service
|
||||
{
|
||||
if (!updatedEmailIds.Contains(email.Id))
|
||||
{
|
||||
var emailObject = _updateLogsHelper.EntityToBsonDocument(email);
|
||||
phoneUpdateLogs.Add(new UpdateLogsObject
|
||||
{
|
||||
EntityId = email.Id.ToString(),
|
||||
UpdatedById = loggedInEmployee.Id.ToString(),
|
||||
OldObject = emailObject,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
});
|
||||
context.ContactsEmails.Remove(email);
|
||||
}
|
||||
}
|
||||
@ -1047,6 +1177,7 @@ namespace Marco.Pms.Services.Service
|
||||
{
|
||||
if (!incomingBucketIds.Contains(contactBucket.BucketId))
|
||||
{
|
||||
|
||||
context.ContactBucketMappings.Remove(contactBucket);
|
||||
}
|
||||
}
|
||||
@ -1163,34 +1294,56 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
|
||||
// Reload updated contact and related data for response, using a fresh context
|
||||
using var responseContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
var reloadedContact = await responseContext.Contacts
|
||||
.Include(c => c.ContactCategory)
|
||||
.FirstOrDefaultAsync(c => c.Id == id && c.IsActive && c.TenantId == tenantId) ?? new Contact();
|
||||
|
||||
var responsePhones = await responseContext.ContactsPhones.AsNoTracking().Where(p => p.ContactId == reloadedContact.Id).ToListAsync();
|
||||
var responseEmails = await responseContext.ContactsEmails.AsNoTracking().Where(e => e.ContactId == reloadedContact.Id).ToListAsync();
|
||||
var responseContactTags = await responseContext.ContactTagMappings.AsNoTracking().Where(t => t.ContactId == reloadedContact.Id).ToListAsync();
|
||||
var responseContactBuckets = await responseContext.ContactBucketMappings.AsNoTracking().Where(cb => cb.ContactId == reloadedContact.Id).ToListAsync();
|
||||
var responseContactProjects = await responseContext.ContactProjectMappings.AsNoTracking().Where(cp => cp.ContactId == reloadedContact.Id).ToListAsync();
|
||||
|
||||
var tagIdsForResponse = responseContactTags.Select(t => t.ContactTagId).Distinct().ToList();
|
||||
var tagsForResponse = await responseContext.ContactTagMasters.Where(t => tagIdsForResponse.Contains(t.Id)).ToListAsync();
|
||||
|
||||
// Map entities to view models
|
||||
var contactVM = reloadedContact.ToContactVMFromContact();
|
||||
|
||||
contactVM.ContactPhones = responsePhones.Select(p => p.ToContactPhoneVMFromContactPhone()).ToList();
|
||||
contactVM.ContactEmails = responseEmails.Select(e => e.ToContactEmailVMFromContactEmail()).ToList();
|
||||
contactVM.Tags = responseContactTags.Select(ctm =>
|
||||
var responseTask = Task.Run(async () =>
|
||||
{
|
||||
var tag = tagsForResponse.Find(t => t.Id == ctm.ContactTagId);
|
||||
return tag != null ? tag.ToContactTagVMFromContactTagMaster() : new ContactTagVM();
|
||||
}).ToList();
|
||||
using var responseContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
contactVM.BucketIds = responseContactBuckets.Select(cb => cb.BucketId).ToList();
|
||||
contactVM.ProjectIds = responseContactProjects.Select(cp => cp.ProjectId).ToList();
|
||||
var reloadedContact = await responseContext.Contacts
|
||||
.Include(c => c.ContactCategory)
|
||||
.FirstOrDefaultAsync(c => c.Id == id && c.IsActive && c.TenantId == tenantId) ?? new Contact();
|
||||
|
||||
var responsePhones = await responseContext.ContactsPhones.AsNoTracking().Where(p => p.ContactId == reloadedContact.Id).ToListAsync();
|
||||
var responseEmails = await responseContext.ContactsEmails.AsNoTracking().Where(e => e.ContactId == reloadedContact.Id).ToListAsync();
|
||||
var responseContactTags = await responseContext.ContactTagMappings.AsNoTracking().Where(t => t.ContactId == reloadedContact.Id).ToListAsync();
|
||||
var responseContactBuckets = await responseContext.ContactBucketMappings.AsNoTracking().Where(cb => cb.ContactId == reloadedContact.Id).ToListAsync();
|
||||
var responseContactProjects = await responseContext.ContactProjectMappings.AsNoTracking().Where(cp => cp.ContactId == reloadedContact.Id).ToListAsync();
|
||||
|
||||
var tagIdsForResponse = responseContactTags.Select(t => t.ContactTagId).Distinct().ToList();
|
||||
var tagsForResponse = await responseContext.ContactTagMasters.Where(t => tagIdsForResponse.Contains(t.Id)).ToListAsync();
|
||||
|
||||
// Map entities to view models
|
||||
var response = reloadedContact.ToContactVMFromContact();
|
||||
|
||||
response.ContactPhones = responsePhones.Select(p => p.ToContactPhoneVMFromContactPhone()).ToList();
|
||||
response.ContactEmails = responseEmails.Select(e => e.ToContactEmailVMFromContactEmail()).ToList();
|
||||
response.Tags = responseContactTags.Select(ctm =>
|
||||
{
|
||||
var tag = tagsForResponse.Find(t => t.Id == ctm.ContactTagId);
|
||||
return tag != null ? tag.ToContactTagVMFromContactTagMaster() : new ContactTagVM();
|
||||
}).ToList();
|
||||
|
||||
response.BucketIds = responseContactBuckets.Select(cb => cb.BucketId).ToList();
|
||||
response.ProjectIds = responseContactProjects.Select(cp => cp.ProjectId).ToList();
|
||||
return response;
|
||||
});
|
||||
|
||||
var contactUpdateLogTask = Task.Run(async () =>
|
||||
{
|
||||
await _updateLogsHelper.PushToUpdateLogsAsync(contactUpdateLog, contactCollection);
|
||||
});
|
||||
var phoneUpdateLogTask = Task.Run(async () =>
|
||||
{
|
||||
await _updateLogsHelper.PushListToUpdateLogsAsync(phoneUpdateLogs, contactPhoneCollection);
|
||||
});
|
||||
var emailUpdateLogTask = Task.Run(async () =>
|
||||
{
|
||||
await _updateLogsHelper.PushListToUpdateLogsAsync(emailUpdateLogs, contactEmailCollection);
|
||||
});
|
||||
|
||||
|
||||
await Task.WhenAll(responseTask, contactUpdateLogTask, phoneUpdateLogTask, emailUpdateLogTask);
|
||||
|
||||
var contactVM = responseTask.Result;
|
||||
|
||||
_logger.LogInfo("Contact {ContactId} successfully updated by employee {EmployeeId}.", contact.Id, loggedInEmployee.Id);
|
||||
|
||||
|
@ -11,6 +11,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
||||
Task<ApiResponse<object>> GetContactsListByBucketIdAsync(Guid bucketId, Guid tenantId, Employee loggedInEmployee);
|
||||
Task<ApiResponse<object>> GetContactProfileAsync(Guid id, Guid tenantId, Employee loggedInEmployee);
|
||||
Task<ApiResponse<object>> GetOrganizationListAsync(Guid tenantId, Employee loggedInEmployee);
|
||||
Task<ApiResponse<object>> GetDesignationListAsync(Guid tenantId, Employee loggedInEmployee);
|
||||
Task<ApiResponse<object>> CreateContactAsync(CreateContactDto createContact, Guid tenantId, Employee loggedInEmployee);
|
||||
Task<ApiResponse<object>> UpdateContactAsync(Guid id, UpdateContactDto updateContact, Guid tenantId, Employee loggedInEmployee);
|
||||
Task<ApiResponse<object>> DeleteContactAsync(Guid id, bool active, Guid tenantId, Employee loggedInEmployee);
|
||||
|
Loading…
x
Reference in New Issue
Block a user