diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityChangeWithUsername.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityChangeWithUsername.cs new file mode 100644 index 0000000000..8f59b2900e --- /dev/null +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/EntityChangeWithUsername.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.AuditLogging +{ + public class EntityChangeWithUsername + { + public EntityChange EntityChange { get; set; } + + public string UserName { get; set; } + } +} \ No newline at end of file diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs index 0e907b2a8e..ad0e0d20c2 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs @@ -69,5 +69,9 @@ namespace Volo.Abp.AuditLogging string entityId = null, string entityTypeFullName = null, CancellationToken cancellationToken = default); + + Task GetEntityChangeWithUsernameAsync(Guid entityChangeId); + + Task> GetEntityChangesWithUsernameAsync(string entityId, string entityTypeFullName); } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs index 80e381e02b..06dcf8ffed 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs @@ -182,6 +182,31 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore return totalCount; } + public virtual async Task GetEntityChangeWithUsernameAsync(Guid entityChangeId) + { + var auditLog = await DbSet.AsNoTracking().IncludeDetails() + .Where(x => x.EntityChanges.Any(y => y.Id == entityChangeId)).FirstAsync(); + + return new EntityChangeWithUsername() + { + EntityChange = auditLog.EntityChanges.First(x => x.Id == entityChangeId), + UserName = auditLog.UserName + }; + } + + public virtual async Task> GetEntityChangesWithUsernameAsync(string entityId, string entityTypeFullName) + { + var query = DbContext.Set() + .AsNoTracking() + .IncludeDetails() + .Where(x => x.EntityId == entityId && x.EntityTypeFullName == entityTypeFullName); + + return await (from e in query + join auditLog in DbSet on e.AuditLogId equals auditLog.Id + select new EntityChangeWithUsername() {EntityChange = e, UserName = auditLog.UserName}) + .OrderByDescending(x => x.EntityChange.ChangeTime).ToListAsync(); + } + protected virtual IQueryable GetEntityChangeListQuery( Guid? auditLogId = null, DateTime? startTime = null, diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs index 3541dfc88d..e2f21bfe24 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs @@ -191,6 +191,35 @@ namespace Volo.Abp.AuditLogging.MongoDB return count; } + public virtual async Task GetEntityChangeWithUsernameAsync(Guid entityChangeId) + { + var auditLog = (await GetMongoQueryable() + .Where(x => x.EntityChanges.Any(y => y.Id == entityChangeId)) + .FirstAsync()); + + return new EntityChangeWithUsername() + { + EntityChange = auditLog.EntityChanges.First(x => x.Id == entityChangeId), + UserName = auditLog.UserName + }; + } + + public virtual async Task> GetEntityChangesWithUsernameAsync(string entityId, string entityTypeFullName) + { + var auditLogs = await GetMongoQueryable() + .Where(x => x.EntityChanges.Any(y => y.EntityId == entityId && y.EntityTypeFullName == entityTypeFullName)) + .As>() + .OrderByDescending(x => x.ExecutionTime) + .ToListAsync(); + + var entityChanges = auditLogs.SelectMany(x => x.EntityChanges).ToList(); + + entityChanges.RemoveAll(x => x.EntityId != entityId || x.EntityTypeFullName != entityTypeFullName); + + return entityChanges.Select(x => new EntityChangeWithUsername() + {EntityChange = x, UserName = auditLogs.First(y => y.Id == x.AuditLogId).UserName}).ToList(); + } + protected virtual IQueryable GetEntityChangeListQuery( Guid? auditLogId = null, DateTime? startTime = null, diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogRepository_Tests.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogRepository_Tests.cs index 6de00a0f49..3214ee75af 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogRepository_Tests.cs +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.TestBase/Volo/Abp/AuditLogging/AuditLogRepository_Tests.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using System.Net; using System.Threading.Tasks; +using Microsoft.VisualBasic; using Shouldly; using Volo.Abp.Auditing; using Volo.Abp.Guids; @@ -761,5 +762,229 @@ namespace Volo.Abp.AuditLogging entityChanges.ShouldNotBeNull(); entityChanges.Count.ShouldBe(1); } + + [Fact] + public async Task GetEntityChangesWithUsernameAsync() + { + // Arrange + var userId = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06"); + var userId2 = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06"); + var ipAddress = "153.1.7.61"; + var firstComment = "first Comment"; + + var firstUser = "Douglas"; + var secondUser = "John Doe"; + + var entityId = Guid.NewGuid().ToString(); + var entityType = "Volo.Abp.AuditLogging.TestEntity"; + + var log1 = new AuditLogInfo + { + UserId = userId, + ImpersonatorUserId = Guid.NewGuid(), + ImpersonatorTenantId = Guid.NewGuid(), + ExecutionTime = DateTime.Today, + ExecutionDuration = 42, + ClientIpAddress = ipAddress, + ClientName = "MyDesktop", + BrowserInfo = "Chrome", + Comments = new List { firstComment, "Second Comment" }, + UserName = firstUser, + EntityChanges = { + new EntityChangeInfo + { + EntityId = entityId, + EntityTypeFullName = entityType, + ChangeType = EntityChangeType.Created, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + }, + new EntityChangeInfo + { + EntityId = Guid.NewGuid().ToString(), + EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity", + ChangeType = EntityChangeType.Created, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + } + + } + }; + + var log2 = new AuditLogInfo + { + UserId = userId2, + ImpersonatorUserId = Guid.NewGuid(), + ImpersonatorTenantId = Guid.NewGuid(), + ExecutionTime = DateTime.Today, + ExecutionDuration = 42, + ClientIpAddress = ipAddress, + ClientName = "MyDesktop", + BrowserInfo = "Chrome", + Comments = new List { firstComment, "Second Comment" }, + HttpStatusCode = (int?)HttpStatusCode.Accepted, + UserName = secondUser, + EntityChanges = { + new EntityChangeInfo + { + EntityId = entityId, + EntityTypeFullName = entityType, + ChangeType = EntityChangeType.Updated, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + } + + } + }; + + await AuditLogRepository.InsertAsync(new AuditLog(GuidGenerator, log1)); + await AuditLogRepository.InsertAsync(new AuditLog(GuidGenerator, log2)); + + //Assert + var entityHistory = await AuditLogRepository.GetEntityChangesWithUsernameAsync(entityId, entityType); + + entityHistory.Count.ShouldBe(2); + var firstUserChange = entityHistory.First(x => x.UserName == firstUser); + firstUserChange.ShouldNotBeNull(); + firstUserChange.EntityChange.ShouldNotBeNull(); + firstUserChange.EntityChange.ChangeType.ShouldBe(EntityChangeType.Created); + + var secondUserChange = entityHistory.First(x => x.UserName == secondUser); + secondUserChange.ShouldNotBeNull(); + secondUserChange.EntityChange.ShouldNotBeNull(); + secondUserChange.EntityChange.ChangeType.ShouldBe(EntityChangeType.Updated); + } + + [Fact] + public async Task GetEntityChangeWithUsernameAsync() + { + // Arrange + var userId = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06"); + var userId2 = new Guid("4456fb0d-74cc-4807-9eee-23e551e6cb06"); + var ipAddress = "153.1.7.61"; + var firstComment = "first Comment"; + + var log1 = new AuditLogInfo + { + UserId = userId, + ImpersonatorUserId = Guid.NewGuid(), + ImpersonatorTenantId = Guid.NewGuid(), + ExecutionTime = DateTime.Today, + ExecutionDuration = 42, + ClientIpAddress = ipAddress, + ClientName = "MyDesktop", + BrowserInfo = "Chrome", + Comments = new List { firstComment, "Second Comment" }, + UserName = "Douglas", + EntityChanges = { + new EntityChangeInfo + { + EntityId = Guid.NewGuid().ToString(), + EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity_Deleted", + ChangeType = EntityChangeType.Deleted, + ChangeTime = new DateTime(1995, 3, 27), + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + }, + new EntityChangeInfo + { + EntityId = Guid.NewGuid().ToString(), + EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity_Created", + ChangeType = EntityChangeType.Created, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + } + + } + }; + + var log2 = new AuditLogInfo + { + UserId = userId2, + ImpersonatorUserId = Guid.NewGuid(), + ImpersonatorTenantId = Guid.NewGuid(), + ExecutionTime = DateTime.Today, + ExecutionDuration = 42, + ClientIpAddress = ipAddress, + ClientName = "MyDesktop", + BrowserInfo = "Chrome", + Comments = new List { firstComment, "Second Comment" }, + HttpStatusCode = (int?)HttpStatusCode.BadGateway, + UserName = "John Doe", + EntityChanges = { + new EntityChangeInfo + { + EntityId = Guid.NewGuid().ToString(), + EntityTypeFullName = "Volo.Abp.AuditLogging.TestEntity_Updated", + ChangeType = EntityChangeType.Updated, + ChangeTime = DateTime.Now, + PropertyChanges = new List + { + new EntityPropertyChangeInfo + { + PropertyTypeFullName = typeof(string).FullName, + PropertyName = "Name", + NewValue = "New value", + OriginalValue = null + } + } + } + } + }; + + await AuditLogRepository.InsertAsync(new AuditLog(GuidGenerator, log1)); + await AuditLogRepository.InsertAsync(new AuditLog(GuidGenerator, log2)); + + var entityChanges = await AuditLogRepository.GetEntityChangeListAsync(); + var entityHistory = + await AuditLogRepository.GetEntityChangeWithUsernameAsync(entityChanges.First().Id); + + entityHistory.EntityChange.ChangeTime.ShouldBe(entityChanges.First().ChangeTime); + entityHistory.UserName.ShouldNotBeNull(); + } } }