Adsing file to delete from S3 in mongoDB while update expenes

This commit is contained in:
ashutosh.nehete 2025-07-23 16:24:59 +05:30
parent 0095cd54f6
commit 4370d5a350
8 changed files with 176 additions and 14 deletions

View File

@ -102,6 +102,16 @@ namespace Marco.Pms.Helpers.CacheHelper
return expense;
}
public async Task<bool> DeleteExpenseFromCacheAsync(Guid id, Guid tenantId)
{
var filter = Builders<ExpenseDetailsMongoDB>.Filter.And(
Builders<ExpenseDetailsMongoDB>.Filter.Eq(e => e.Id, id.ToString()),
Builders<ExpenseDetailsMongoDB>.Filter.Eq(e => e.TenantId, tenantId.ToString())
);
var result = await _collection.DeleteOneAsync(filter);
return result.DeletedCount > 0;
}
private async Task InitializeCollectionAsync()
{
var indexKeys = Builders<ExpenseDetailsMongoDB>.IndexKeys.Ascending(x => x.ExpireAt);

View File

@ -1,21 +1,28 @@
using Marco.Pms.Model.MongoDBModels.Utility;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using System.Collections;
namespace Marco.Pms.Helpers
namespace Marco.Pms.Helpers.Utility
{
public class UpdateLogHelper
public class UtilityMongoDBHelper
{
private readonly IMongoDatabase _mongoDatabase;
public UpdateLogHelper(IConfiguration configuration)
private readonly IConfiguration _configuration;
private readonly ILogger<UtilityMongoDBHelper> _logger;
public UtilityMongoDBHelper(IConfiguration configuration, ILogger<UtilityMongoDBHelper> logger)
{
_configuration = configuration;
_logger = logger;
var connectionString = configuration["MongoDB:ModificationConnectionString"];
var mongoUrl = new MongoUrl(connectionString);
var client = new MongoClient(mongoUrl); // Your MongoDB connection string
_mongoDatabase = client.GetDatabase(mongoUrl.DatabaseName); // Your MongoDB Database name
}
#region =================================================================== Update Log Helper Functions ===================================================================
public async Task PushToUpdateLogsAsync(UpdateLogsObject oldObject, string collectionName)
{
var collection = _mongoDatabase.GetCollection<UpdateLogsObject>(collectionName);
@ -87,5 +94,35 @@ namespace Marco.Pms.Helpers
return bson;
}
#endregion
#region =================================================================== S3 deletion Helper Functions ===================================================================
public async Task PushToS3DeletionAsync(List<S3DeletionObject> deletionObject)
{
var bucketName = _configuration["AWS:BucketName"];
if (bucketName != null)
{
deletionObject = deletionObject.Select(d => new S3DeletionObject
{
BucketName = bucketName,
Key = d.Key,
Deleted = false
}).ToList();
}
_logger.LogInformation("Delection object for bucket {BucketName} added to mongoDB", bucketName);
try
{
var collection = _mongoDatabase.GetCollection<S3DeletionObject>("S3Delection");
await collection.InsertManyAsync(deletionObject);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occured while saving delection object for S3 to MogoDB");
}
_logger.LogInformation("Delection Objects added to MongoDB Successfully");
}
#endregion
}
}

View File

@ -0,0 +1,15 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Marco.Pms.Model.MongoDBModels.Utility
{
public class S3DeletionObject
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; } = Guid.NewGuid();
public string BucketName { get; set; } = string.Empty;
public string Key { get; set; } = string.Empty;
public bool Deleted { get; set; } = false;
}
}

View File

@ -2,10 +2,12 @@
{
public class FileUploadModel
{
public Guid? DocumentId { get; set; }
public string? FileName { get; set; } // Name of the file (e.g., "image1.png")
public string? Base64Data { get; set; } // Base64-encoded string of the file
public string? ContentType { get; set; } // MIME type (e.g., "image/png", "application/pdf")
public long FileSize { get; set; } // File size in bytes
public string? Description { get; set; } // Optional: Description or purpose of the file
public bool IsActive { get; set; } = true;
}
}

View File

@ -1026,12 +1026,49 @@ namespace Marco.Pms.Services.Helpers
public async Task<ExpenseDetailsMongoDB?> GetExpenseDetailsById(Guid id, Guid tenantId)
{
var response = await _expenseCache.GetExpenseDetailsByIdAsync(id, tenantId);
if (response == null || response.Id == string.Empty)
try
{
var response = await _expenseCache.GetExpenseDetailsByIdAsync(id, tenantId);
if (response != null && response.Id != string.Empty)
{
return response;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occured while fetching expense details from cache");
}
return null;
}
return response;
public async Task ReplaceExpenseAsync(Expenses expense)
{
bool response = false;
try
{
response = await _expenseCache.DeleteExpenseFromCacheAsync(expense.Id, expense.TenantId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occured while deleting expense from cache");
}
if (response)
{
await AddExpenseByObjectAsync(expense);
}
}
public async Task DeleteExpenseAsync(Guid id, Guid tenantId)
{
try
{
var response = await _expenseCache.DeleteExpenseFromCacheAsync(id, tenantId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occured while deleting expense from cache");
}
}
#endregion

View File

@ -1,6 +1,7 @@
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Helpers;
using Marco.Pms.Helpers.CacheHelper;
using Marco.Pms.Helpers.Utility;
using Marco.Pms.Model.Authentication;
using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Utilities;
@ -186,7 +187,7 @@ builder.Services.AddScoped<DirectoryHelper>();
builder.Services.AddScoped<MasterHelper>();
builder.Services.AddScoped<ReportHelper>();
builder.Services.AddScoped<CacheUpdateHelper>();
builder.Services.AddScoped<UpdateLogHelper>();
builder.Services.AddScoped<UtilityMongoDBHelper>();
#endregion
#region Cache Services

View File

@ -1,6 +1,6 @@
using AutoMapper;
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Helpers;
using Marco.Pms.Helpers.Utility;
using Marco.Pms.Model.Dtos.Expenses;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements;
@ -30,7 +30,7 @@ namespace Marco.Pms.Services.Service
private readonly ILoggingService _logger;
private readonly S3UploadService _s3Service;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly UpdateLogHelper _updateLogHelper;
private readonly UtilityMongoDBHelper _updateLogHelper;
private readonly CacheUpdateHelper _cache;
private readonly IMapper _mapper;
private static readonly Guid Draft = Guid.Parse("297e0d8f-f668-41b5-bfea-e03b354251c8");
@ -40,7 +40,7 @@ namespace Marco.Pms.Services.Service
IDbContextFactory<ApplicationDbContext> dbContextFactory,
ApplicationDbContext context,
IServiceScopeFactory serviceScopeFactory,
UpdateLogHelper updateLogHelper,
UtilityMongoDBHelper updateLogHelper,
CacheUpdateHelper cache,
ILoggingService logger,
S3UploadService s3Service,
@ -690,6 +690,63 @@ namespace Marco.Pms.Services.Service
_logger.LogError(ex, "Concurrency conflict while updating project {ProjectId} ", id);
return ApiResponse<object>.ErrorResponse("Conflict occurred.", "This project has been modified by someone else. Please refresh and try again.", 409);
}
if (model.BillAttachments?.Any() ?? false)
{
var newBillAttachments = model.BillAttachments.Where(ba => ba.DocumentId == null && ba.IsActive).ToList();
if (newBillAttachments.Any())
{
await ProcessAndUploadAttachmentsAsync(newBillAttachments, existingExpense, loggedInEmployee.Id, tenantId);
await _context.SaveChangesAsync();
}
var deleteBillAttachments = model.BillAttachments.Where(ba => ba.DocumentId != null && !ba.IsActive).ToList();
if (deleteBillAttachments.Any())
{
var documentIds = deleteBillAttachments.Select(d => d.DocumentId).ToList();
var attachmentTask = Task.Run(async () =>
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
var attachments = await dbContext.BillAttachments.AsNoTracking().Where(ba => documentIds.Contains(ba.DocumentId)).ToListAsync();
dbContext.BillAttachments.RemoveRange(attachments);
await dbContext.SaveChangesAsync();
});
var documentsTask = Task.Run(async () =>
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
var documents = await dbContext.Documents.AsNoTracking().Where(ba => documentIds.Contains(ba.Id)).ToListAsync();
if (documents.Any())
{
dbContext.Documents.RemoveRange(documents);
await dbContext.SaveChangesAsync();
List<S3DeletionObject> deletionObject = new List<S3DeletionObject>();
foreach (var document in documents)
{
deletionObject.Add(new S3DeletionObject
{
Key = document.S3Key
});
if (!string.IsNullOrWhiteSpace(document.ThumbS3Key) && document.ThumbS3Key != document.S3Key)
{
deletionObject.Add(new S3DeletionObject
{
Key = document.ThumbS3Key
});
}
}
await _updateLogHelper.PushToS3DeletionAsync(deletionObject);
}
});
await Task.WhenAll(attachmentTask, documentsTask);
}
}
try
{
// Task to save the detailed audit log to a separate system (e.g., MongoDB).
@ -718,9 +775,11 @@ namespace Marco.Pms.Services.Service
});
}).Unwrap();
await Task.WhenAll(mongoDBTask, getNextStatusesTask);
var cacheUpdateTask = _cache.ReplaceExpenseAsync(existingExpense);
var nextPossibleStatuses = await getNextStatusesTask;
await Task.WhenAll(mongoDBTask, getNextStatusesTask, cacheUpdateTask);
var nextPossibleStatuses = getNextStatusesTask.Result;
var response = _mapper.Map<ExpenseList>(existingExpense);
if (nextPossibleStatuses != null)

View File

@ -42,7 +42,8 @@ namespace Marco.Pms.Services.Service
if (allowedFilesType == null || !allowedFilesType.Contains(fileType))
{
_logger.LogWarning("Unsupported file type. {FileType}", fileType);
throw new InvalidOperationException("Unsupported file type.");
//throw new InvalidOperationException("Unsupported file type.");
return;
}
fileBytes = Convert.FromBase64String(base64);