add girIgnore
This commit is contained in:
Executable
+475
@@ -0,0 +1,475 @@
|
||||
using MongoDB.Bson.IO;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using JsonConvert = Newtonsoft.Json.JsonConvert;
|
||||
|
||||
namespace audit_logs.Services;
|
||||
|
||||
using System.Security.Claims;
|
||||
using System.Text.RegularExpressions;
|
||||
using audit_logs.Models;
|
||||
using audit_logs.Models.AppSettings;
|
||||
using audit_logs.Models.DTO;
|
||||
using audit_logs.Services.Interfaces;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Bson;
|
||||
using audit_logs.Repositories;
|
||||
using MongoDB.Driver;
|
||||
using audit_logs.Utils;
|
||||
using audit.Model;
|
||||
using audit_logs.Repositories.Interfaces;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class AuditService : IAuditService
|
||||
{
|
||||
private readonly IAuditRecordRepository _auditLogRepository;
|
||||
|
||||
public AuditService(IAuditRecordRepository auditLogRepository)
|
||||
{
|
||||
_auditLogRepository = auditLogRepository;
|
||||
}
|
||||
|
||||
public AuditService(IMongoDatabase database)
|
||||
{
|
||||
_auditLogRepository = new AuditRecordRepository(database);
|
||||
}
|
||||
|
||||
public AuditService(IOptions<MongoDbSettingsAudit> dbSettings)
|
||||
{
|
||||
_auditLogRepository = new AuditRecordRepository(dbSettings);
|
||||
}
|
||||
|
||||
public IAuditRecordRepository AuditLogRepository => _auditLogRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Records a detailed audit log entry.
|
||||
/// </summary>
|
||||
/// <param name="auditRecord">The data required to create an audit record.</param>
|
||||
public async Task RecordAuditAsync(AuditRecord auditRecord)
|
||||
{
|
||||
if (auditRecord == null) throw new ArgumentNullException(nameof(auditRecord));
|
||||
if (string.IsNullOrWhiteSpace(auditRecord.EntityType))
|
||||
throw new ArgumentNullException(nameof(auditRecord.EntityType));
|
||||
if (string.IsNullOrWhiteSpace(auditRecord.RecordId))
|
||||
throw new ArgumentNullException(nameof(auditRecord.RecordId));
|
||||
if (string.IsNullOrWhiteSpace(auditRecord.UserId))
|
||||
throw new ArgumentNullException(nameof(auditRecord.UserId));
|
||||
if (string.IsNullOrWhiteSpace(auditRecord.ActionType))
|
||||
throw new ArgumentNullException(nameof(auditRecord.ActionType));
|
||||
if (auditRecord.Changes == null) throw new ArgumentNullException(nameof(auditRecord.Changes));
|
||||
if (string.IsNullOrWhiteSpace(auditRecord.UserIpAddress))
|
||||
throw new ArgumentNullException(nameof(auditRecord.UserIpAddress));
|
||||
|
||||
await _auditLogRepository.InsertOneAsync(auditRecord);
|
||||
}
|
||||
|
||||
public Task<T> JsonDeepCopyAsync<T>(T source)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var serialized = JsonConvert.SerializeObject(source);
|
||||
var deserialized = JsonConvert.DeserializeObject<T>(serialized);
|
||||
if (deserialized == null)
|
||||
throw new InvalidOperationException("Deserialization resulted in null");
|
||||
return deserialized;
|
||||
});
|
||||
}
|
||||
public Task<T> DeepCopyAsync<T>(T source)
|
||||
{
|
||||
T copy;
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
// Serializa el objeto al stream en formato BSON
|
||||
using (var writer = new BsonBinaryWriter(stream))
|
||||
{
|
||||
BsonSerializer.Serialize(writer, source);
|
||||
}
|
||||
|
||||
// Reinicia la posición del stream para leer desde el inicio
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Deserializa el objeto desde el stream
|
||||
using (var reader = new BsonBinaryReader(stream))
|
||||
{
|
||||
copy = BsonSerializer.Deserialize<T>(reader);
|
||||
}
|
||||
}
|
||||
return Task.FromResult(copy);
|
||||
}
|
||||
|
||||
#region Documentacioòn para el metodo CreateAuditLogAsync
|
||||
|
||||
/// <summary>
|
||||
/// Creates an audit log record based on the provided original and modified data.
|
||||
/// This method generates an <see cref="AuditRecord"/> object to encapsulate all necessary details for the audit log entry.
|
||||
/// </summary>
|
||||
/// <param name="user">
|
||||
/// The <see cref="ClaimsPrincipal"/> representing the user performing the action.
|
||||
/// This parameter is required and is used to populate the <c>UserId</c> and optionally other user-related fields.
|
||||
/// </param>
|
||||
/// <param name="dataOriginal">
|
||||
/// The original object before the change. This parameter can be null for new records (when <c>ActionType</c> is "create").
|
||||
/// </param>
|
||||
/// <param name="dataModified">
|
||||
/// The modified object after the change. This parameter can be null for deleted records (when <c>ActionType</c> is "delete").
|
||||
/// </param>
|
||||
/// <param name="reason">
|
||||
/// The reason for the change. This parameter is optional; if null or empty, it defaults to "Not specified".
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Thrown if <paramref name="user"/> is null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// This method constructs and populates an <see cref="AuditRecord"/> object that contains the following fields:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <description><c>EntityType</c>: The type of entity being audited (e.g., "Patient").</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><c>RecordId</c>: The unique identifier of the record being modified within the entity (e.g. patient.id, nhc,etc) <c>EntityType</c>.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><c>UserId</c>: The ID of the user who performed the action that triggered the audit entry.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><c>ActionType</c>: The type of action performed, which can be one of the following:
|
||||
/// <list type="bullet">
|
||||
/// <item><description><c>"create"</c>: Indicates a new record was created.</description></item>
|
||||
/// <item><description><c>"update"</c>: Indicates an existing record was updated.</description></item>
|
||||
/// <item><description><c>"delete"</c>: Indicates a record was removed.</description></item>
|
||||
/// </list>
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><c>Reason</c>: (Optional) A description or justification for the action being logged.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><c>ActionTime</c>: The timestamp of when the action occurred, represented in UTC.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><c>Changes</c>: A list detailing the differences between the original and modified data. Each change includes:
|
||||
/// <list type="bullet">
|
||||
/// <item><description><c>Field</c>: The name of the field that was changed.</description></item>
|
||||
/// <item><description><c>OldValue</c>: The value of the field before the change.</description></item>
|
||||
/// <item><description><c>NewValue</c>: The value of the field after the change.</description></item>
|
||||
/// <item><description><c>ValueType</c>: The data type of the field (e.g., "string", "int", "bool").</description></item>
|
||||
/// </list>
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><c>UserIpAddress</c>: (Optional) The IP address of the user performing the action. Useful for tracking location-based information.</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// Once the <see cref="AuditRecord"/> is constructed, it is asynchronously saved using <c>RecordAuditAsync</c>.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous operation of recording the audit log. The task completes when the audit record is successfully stored.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// Here is an example of how to use this method:
|
||||
/// <code>
|
||||
/// await CreateAuditLogAsync(
|
||||
/// user: HttpContext.User,
|
||||
/// dataOriginal: oldData,
|
||||
/// dataModified: newData,
|
||||
/// reason: "Updated user details"
|
||||
/// );
|
||||
/// </code>
|
||||
/// </example>
|
||||
#endregion
|
||||
|
||||
public async Task CreateAuditLogAsync(ClaimsPrincipal? user, Object? dataOriginal, Object? dataModified,
|
||||
string? reason)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
Log.Error("An error occurred: User is null");
|
||||
return;
|
||||
}
|
||||
|
||||
AuditRecord auditLogData = new AuditRecord();
|
||||
JsonComparer jsonComparer = new();
|
||||
|
||||
if (dataOriginal != null)
|
||||
{
|
||||
auditLogData.EntityType =
|
||||
IsAnonymousType(dataOriginal.GetType()) ? "AnonymousType" : dataOriginal.GetType().Name;
|
||||
|
||||
var idProperty = dataOriginal.GetType().GetProperty("Id");
|
||||
|
||||
if (idProperty != null && idProperty.PropertyType == typeof(ObjectId))
|
||||
{
|
||||
ObjectId id = (ObjectId)idProperty.GetValue(dataOriginal);
|
||||
auditLogData.RecordId = id.ToString();
|
||||
|
||||
}else if (idProperty != null && idProperty.PropertyType == typeof(string))
|
||||
{
|
||||
auditLogData.RecordId = idProperty.GetValue(dataOriginal).ToString();
|
||||
}
|
||||
}
|
||||
else if (dataModified != null)
|
||||
{
|
||||
auditLogData.EntityType =
|
||||
IsAnonymousType(dataModified.GetType()) ? "AnonymousType" : dataModified.GetType().Name;
|
||||
var idProperty = dataModified.GetType().GetProperty("Id");
|
||||
if (idProperty != null && idProperty.PropertyType == typeof(ObjectId))
|
||||
{
|
||||
ObjectId id = (ObjectId)idProperty.GetValue(dataModified);
|
||||
//TODO verificar si esta bien obtener el valor asi:
|
||||
auditLogData.RecordId = id.ToString();
|
||||
}else if (idProperty != null && idProperty.PropertyType == typeof(string))
|
||||
{
|
||||
auditLogData.RecordId = idProperty.GetValue(dataModified).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
auditLogData.ActionTime = DateTime.Now;
|
||||
auditLogData.Reason = string.IsNullOrEmpty(reason) ? "Not specified" : reason;
|
||||
auditLogData.UserIpAddress = string.IsNullOrEmpty(user.FindFirst("IpAddress")?.Value)
|
||||
? "Not specified"
|
||||
: user.FindFirst("IpAddress")?.Value ?? "Not specified";
|
||||
auditLogData.UserId = user.FindFirst(ClaimTypes.Name)?.Value ?? "Not specified";
|
||||
// Asignar ActionType segun las condiciones
|
||||
if (dataOriginal == null || string.IsNullOrEmpty(dataOriginal.ToString()))
|
||||
{
|
||||
auditLogData.ActionType = "create";
|
||||
}
|
||||
else if (dataModified == null || string.IsNullOrEmpty(dataModified.ToString()))
|
||||
{
|
||||
auditLogData.ActionType = "delete";
|
||||
}
|
||||
else
|
||||
{
|
||||
auditLogData.ActionType = "update";
|
||||
}
|
||||
// dataOriginal="{\n \"Id\": \"PV8\",\n \"Items\": [\n {\n \"Code\": \"Temperature\",\n \"CodingSystem\": \"SENSOR\",\n \"OriginalName\": \"Room_Temperature\",\n \"ParentCode\": \"T\",\n \"ParentCodingSystem\": \"Room_Temperature\",\n \"ParentName\": null,\n \"Name\": \"Room_Temperature\",\n \"Units\": \"T\",\n \"ArrowType\": 1,\n \"ShowArrow\": true,\n \"ForceUnits\": false,\n \"MinAlert\": null,\n \"MaxAlert\": null,\n \"MaxWarn\": null,\n \"MinWarn\": null,\n \"WarnColor\": null,\n \"AlertColor\": null,\n \"AlertValues\": null,\n \"WarningValues\": null,\n \"ForceWarn\": false,\n \"ForceAlert\": false,\n \"Alert\": null,\n \"Expires\": null,\n \"ShowOnExpired\": false,\n \"Persist\": null,\n \"ColorOnExpired\": null,\n \"RetentionPolicy\": null,\n \"RetentionPolicyValue\": null,\n \"LevelCondition\": null,\n \"UiConfiguration\": {\n \"screenLabel\": \"screenLabel\"\n },\n \"Alarm\": null,\n \"Description\": null,\n \"RequiredValue\": null,\n \"Preconditions\": null,\n \"CheckObservations\": false,\n \"CreateObservation\": null,\n \"TimeFromMessageTime\": false\n }\n ]\n}"
|
||||
//dataModified = "{\n \"Id\": \"PV8\",\n \"Items\": [\n {\n \"Code\": \"Temperature\",\n \"CodingSystem\": \"SENSOR\",\n \"OriginalName\": \"Room_Temperature\",\n \"ParentCode\": \"T\",\n \"ParentCodingSystem\": \"Room_Temperature\",\n \"ParentName\": null,\n \"Name\": \"Room_Temperature\",\n \"Units\": \"T\",\n \"ArrowType\": 1,\n \"ShowArrow\": true,\n \"ForceUnits\": false,\n \"MinAlert\": null,\n \"MaxAlert\": null,\n \"MaxWarn\": null,\n \"MinWarn\": null,\n \"WarnColor\": null,\n \"AlertColor\": null,\n \"AlertValues\": null,\n \"WarningValues\": null,\n \"ForceWarn\": false,\n \"ForceAlert\": false,\n \"Alert\": null,\n \"Expires\": null,\n \"ShowOnExpired\": false,\n \"Persist\": null,\n \"ColorOnExpired\": null,\n \"RetentionPolicy\": null,\n \"RetentionPolicyValue\": null,\n \"LevelCondition\": null,\n \"UiConfiguration\": {\n \"screenLabel\": \"screenLabel\"\n },\n \"Alarm\": null,\n \"Description\": null,\n \"RequiredValue\": null,\n \"Preconditions\": null,\n \"CheckObservations\": false,\n \"CreateObservation\": null,\n \"TimeFromMessageTime\": false\n },\n {\n \"Code\": \"88\",\n \"CodingSystem\": \"88\",\n \"OriginalName\": \"88\",\n \"ParentCode\": \"88\",\n \"ParentCodingSystem\": \"88\",\n \"ParentName\": null,\n \"Name\": \"88\",\n \"Units\": \"88\",\n \"ArrowType\": 1,\n \"ShowArrow\": true,\n \"ForceUnits\": false,\n \"MinAlert\": null,\n \"MaxAlert\": null,\n \"MaxWarn\": null,\n \"MinWarn\": null,\n \"WarnColor\": null,\n \"AlertColor\": null,\n \"AlertValues\": null,\n \"WarningValues\": null,\n \"ForceWarn\": false,\n \"ForceAlert\": false,\n \"Alert\": null,\n \"Expires\": null,\n \"ShowOnExpired\": false,\n \"Persist\": null,\n \"ColorOnExpired\": null,\n \"RetentionPolicy\": null,\n \"RetentionPolicyValue\": null,\n \"LevelCondition\": null,\n \"UiConfiguration\": {\n \"screenLabel\": \"88\"\n },\n \"Alarm\": null,\n \"Description\": null,\n \"RequiredValue\": null,\n \"Preconditions\": null,\n \"CheckObservations\": false,\n \"CreateObservation\": null,\n \"TimeFromMessageTime\": false\n }\n ]\n}";
|
||||
|
||||
auditLogData.Changes = jsonComparer.GetDifferences(dataOriginal, dataModified);
|
||||
await RecordAuditAsync(auditLogData);
|
||||
}
|
||||
|
||||
// Método auxiliar para verificar si un tipo es anonimo, solo es necesario para pruebas
|
||||
private bool IsAnonymousType(Type type)
|
||||
{
|
||||
return type.IsGenericType && type.Name.StartsWith("<>f__AnonymousType");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and records an audit log entry based on the differences between the original and modified data provided in an <see cref="AuditLogData"/> object.
|
||||
/// </summary>
|
||||
/// <param name="auditLogData">An object containing all necessary details for the audit log entry, including:</param>
|
||||
/// <param name="auditLogData.EntityType">The type of entity being modified.</param>
|
||||
/// <param name="auditLogData.RecordId">The unique ID of the record being changed.</param>
|
||||
/// <param name="auditLogData.UserId">The ID of the user performing the action.</param>
|
||||
/// <param name="auditLogData.ActionType">The type of action performed.</param>
|
||||
/// <param name="auditLogData.Reason">The reason for the action.</param>
|
||||
/// <param name="auditLogData.ActionTime">The timestamp of the action.</param>
|
||||
/// <param name="auditLogData.OriginalJson">The original JSON data.</param>
|
||||
/// <param name="auditLogData.ModifiedJson">The modified JSON data.</param>
|
||||
/// <param name="auditLogData.UserIpAddress">The IP address of the user.</param>
|
||||
public async Task CreateAuditLogAsync(AuditLogData auditLogData)
|
||||
{
|
||||
if (auditLogData == null) throw new ArgumentNullException(nameof(auditLogData));
|
||||
AuditRecord auditRecord = new AuditRecord();
|
||||
auditRecord.EntityType = auditLogData.EntityType;
|
||||
auditRecord.RecordId = auditLogData.RecordId;
|
||||
auditRecord.UserId = auditLogData.UserId;
|
||||
auditRecord.ActionTime = auditLogData.ActionTime;
|
||||
auditRecord.ActionType = auditLogData.ActionType;
|
||||
auditRecord.Reason = auditLogData.Reason;
|
||||
auditRecord.UserIpAddress = auditLogData.UserIpAddress;
|
||||
JsonComparer jsonComparer = new();
|
||||
auditRecord.Changes = jsonComparer.GetDifferences(auditLogData.OriginalJson, auditLogData.ModifiedJson);
|
||||
await RecordAuditAsync(auditRecord);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves paginated audit logs.
|
||||
/// </summary>
|
||||
/// <param name="filterDto.pageNumber">The page number to retrieve (1-based).</param>
|
||||
/// <param name="filterDto.pageSize">The number of records per page.</param>
|
||||
/// <param name="filterDto.userId">filter taking into account the user who made the modification.</param>
|
||||
/// <param name="filterDto.startDate">filter by the date the modification was made.</param>
|
||||
/// <param name="filterDto.endDate">filter by the date the modification was made.</param>
|
||||
/// <param name="filterDto.recordId">filter by modified record id in the collection.</param>
|
||||
/// <returns>A paginated list of audit records.</returns>
|
||||
public async Task<PaginatedLogsResultDto<AuditRecordDto>> GetAuditLogsBySpecificFilterAsync(
|
||||
SpecificAuditLogFilterDto filterDto)
|
||||
{
|
||||
if (filterDto.pageNumber <= 0)
|
||||
throw new ArgumentException("Page number must be greater than 0.", nameof(filterDto.pageNumber));
|
||||
if (filterDto.pageSize <= 0)
|
||||
throw new ArgumentException("Page size must be greater than 0.", nameof(filterDto.pageSize));
|
||||
|
||||
// Construir filtro dinámico
|
||||
var filter = BuildAuditLogFilter(filterDto.userId, filterDto.recordId, filterDto.entityType,
|
||||
filterDto.startDate, filterDto.endDate, filterDto.actionTime);
|
||||
|
||||
var totalRecords = await _auditLogRepository.CountDocumentsAsync(filter);
|
||||
var totalPages = (int)Math.Ceiling((double)totalRecords / filterDto.pageSize);
|
||||
var sort = Builders<AuditRecord>.Sort.Descending(x => x.ActionTime); // Ordenar por fecha más reciente
|
||||
var records = await _auditLogRepository.GetPaginatedAsync(filterDto.pageNumber, filterDto.pageSize, filter, sort);
|
||||
|
||||
|
||||
// var records = await _auditLogRepository.GetPaginatedAsync(filterDto.pageNumber, filterDto.pageSize, filter);
|
||||
var recordsDto = records.Select(record => AuditRecordMapper.ToDto(record)).ToList();
|
||||
|
||||
return new PaginatedLogsResultDto<AuditRecordDto>
|
||||
{
|
||||
CurrentPage = filterDto.pageNumber,
|
||||
PageSize = filterDto.pageSize,
|
||||
TotalRecords = totalRecords,
|
||||
TotalPages = totalPages,
|
||||
Records = recordsDto
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves paginated audit logs with optional full-text search across multiple fields.
|
||||
/// </summary>
|
||||
/// <param name="filtertTextOrDateDto.pageNumber">The page number to retrieve (1-based).</param>
|
||||
/// <param name="filtertTextOrDateDto.pageSize">The number of records per page.</param>
|
||||
/// <param name="filtertTextOrDateDto.text_to_search">Text to search in any field.</param>
|
||||
/// /// <param name="filtertTextOrDateDto.startDate">filter by the date the modification was made.</param>
|
||||
/// <param name="filtertTextOrDateDto.endDate">filter by the date the modification was made.</param>
|
||||
/// <returns>A paginated list of audit records.</returns>
|
||||
public async Task<PaginatedLogsResultDto<AuditRecordDto>> GetAuditLogsMatchingTextOrDateAsync(
|
||||
AuditLogTextOrDateSearchDTO filtertTextOrDateDto)
|
||||
{
|
||||
if (filtertTextOrDateDto.pageNumber <= 0)
|
||||
throw new ArgumentException("Page number must be greater than 0.", nameof(filtertTextOrDateDto.pageNumber));
|
||||
if (filtertTextOrDateDto.pageSize <= 0)
|
||||
throw new ArgumentException("Page size must be greater than 0.", nameof(filtertTextOrDateDto.pageSize));
|
||||
|
||||
// Construct the text search filter if text_to_search is provided
|
||||
FilterDefinition<AuditRecord> filter;
|
||||
if (!string.IsNullOrWhiteSpace(filtertTextOrDateDto.searchText) || filtertTextOrDateDto.startDate.HasValue ||
|
||||
filtertTextOrDateDto.endDate.HasValue || filtertTextOrDateDto.actionTime.HasValue)
|
||||
{
|
||||
filter = BuildAuditLogFilterByText(filtertTextOrDateDto.searchText, filtertTextOrDateDto.startDate,
|
||||
filtertTextOrDateDto.endDate, filtertTextOrDateDto.actionTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
filter = Builders<AuditRecord>.Filter.Empty;
|
||||
}
|
||||
|
||||
var totalRecords = await _auditLogRepository.CountDocumentsAsync(filter);
|
||||
var totalPages = (int)Math.Ceiling((double)totalRecords / filtertTextOrDateDto.pageSize);
|
||||
var sort = Builders<AuditRecord>.Sort.Descending(x => x.ActionTime); // Ordenar por fecha más reciente
|
||||
|
||||
var records = await _auditLogRepository.GetPaginatedAsync(filtertTextOrDateDto.pageNumber,
|
||||
filtertTextOrDateDto.pageSize, filter,sort);
|
||||
var recordsDto = records.Select(record => AuditRecordMapper.ToDto(record)).ToList();
|
||||
|
||||
return new PaginatedLogsResultDto<AuditRecordDto>
|
||||
{
|
||||
CurrentPage = filtertTextOrDateDto.pageNumber,
|
||||
PageSize = filtertTextOrDateDto.pageSize,
|
||||
TotalRecords = totalRecords,
|
||||
TotalPages = totalPages,
|
||||
Records = recordsDto
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static FilterDefinition<AuditRecord> BuildAuditLogFilter(
|
||||
string? userId,
|
||||
string? recordId,
|
||||
string? entityType,
|
||||
DateTime? startDate,
|
||||
DateTime? endDate,
|
||||
DateTime? actionTime)
|
||||
{
|
||||
var builder = Builders<AuditRecord>.Filter;
|
||||
var filters = new List<FilterDefinition<AuditRecord>>();
|
||||
|
||||
if (!string.IsNullOrEmpty(userId))
|
||||
{
|
||||
filters.Add(builder.Eq(x => x.UserId, userId));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(recordId))
|
||||
{
|
||||
filters.Add(builder.Eq(x => x.RecordId, recordId));
|
||||
}
|
||||
|
||||
if (startDate.HasValue)
|
||||
{
|
||||
filters.Add(builder.Gte(x => x.ActionTime, startDate.Value));
|
||||
}
|
||||
|
||||
if (endDate.HasValue)
|
||||
{
|
||||
filters.Add(builder.Lte(x => x.ActionTime, endDate.Value));
|
||||
}
|
||||
if (actionTime.HasValue)
|
||||
{
|
||||
filters.Add(builder.Eq(x => x.ActionTime, actionTime));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entityType))
|
||||
{
|
||||
filters.Add(builder.Eq(x => x.EntityType, entityType));
|
||||
}
|
||||
|
||||
return filters.Count > 0 ? builder.And(filters) : builder.Empty;
|
||||
}
|
||||
|
||||
|
||||
private static FilterDefinition<AuditRecord> BuildAuditLogFilterByText(
|
||||
string? searchText,
|
||||
DateTime? startDate = null,
|
||||
DateTime? endDate = null,
|
||||
DateTime? actionTime = null)
|
||||
{
|
||||
var builder = Builders<AuditRecord>.Filter;
|
||||
var filters = new List<FilterDefinition<AuditRecord>>();
|
||||
|
||||
// Filtro por rango de fechas
|
||||
if (startDate.HasValue && endDate.HasValue)
|
||||
{
|
||||
filters.Add(builder.And(
|
||||
builder.Gte(x => x.ActionTime, startDate.Value),
|
||||
builder.Lte(x => x.ActionTime, endDate.Value)
|
||||
));
|
||||
}
|
||||
else if (startDate.HasValue)
|
||||
{
|
||||
filters.Add(builder.Gte(x => x.ActionTime, startDate.Value));
|
||||
}
|
||||
else if (endDate.HasValue)
|
||||
{
|
||||
filters.Add(builder.Lte(x => x.ActionTime, endDate.Value));
|
||||
}
|
||||
else if (actionTime.HasValue)
|
||||
{
|
||||
filters.Add(builder.Eq(x => x.ActionTime, actionTime));
|
||||
}
|
||||
|
||||
// Filtro por texto si se proporciona
|
||||
if (!string.IsNullOrEmpty(searchText))
|
||||
{
|
||||
var regexFilter = new BsonRegularExpression(new Regex(searchText, RegexOptions.IgnoreCase));
|
||||
filters.Add(builder.Or(
|
||||
builder.Regex(x => x.UserId, regexFilter),
|
||||
builder.Regex(x => x.RecordId, regexFilter),
|
||||
builder.Regex(x => x.EntityType, regexFilter),
|
||||
builder.Regex(x => x.ActionType, regexFilter)
|
||||
));
|
||||
}
|
||||
|
||||
// Combina todos los filtros con AND
|
||||
if (filters.Any())
|
||||
{
|
||||
return builder.And(filters);
|
||||
}
|
||||
|
||||
return builder.Empty; // Sin filtros, devuelve todos los documentos
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using audit_logs.Services.Interfaces;
|
||||
using MongoDB.Bson.IO;
|
||||
using MongoDB.Bson.Serialization;
|
||||
|
||||
namespace audit_logs.Services
|
||||
{
|
||||
public class DeepCopyService : IDeepCopyService
|
||||
{
|
||||
public Task<T> DeepCopyAsync<T>(T source)
|
||||
{
|
||||
T copy;
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
// Serializa el objeto al stream en formato BSON
|
||||
using (var writer = new BsonBinaryWriter(stream))
|
||||
{
|
||||
BsonSerializer.Serialize(writer, source);
|
||||
}
|
||||
|
||||
// Reinicia la posición del stream para leer desde el inicio
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Deserializa el objeto desde el stream
|
||||
using (var reader = new BsonBinaryReader(stream))
|
||||
{
|
||||
copy = BsonSerializer.Deserialize<T>(reader);
|
||||
}
|
||||
}
|
||||
return Task.FromResult(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using audit_logs.Models.DTO;
|
||||
using audit.Model;
|
||||
|
||||
namespace audit_logs.Services.Interfaces;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using audit_logs.Models;
|
||||
using MongoDB.Bson;
|
||||
public interface IAuditService
|
||||
{
|
||||
Task RecordAuditAsync(AuditRecord auditRecord);
|
||||
Task CreateAuditLogAsync(ClaimsPrincipal? user, object dataOriginal, object dataModified, string reason = null);
|
||||
Task CreateAuditLogAsync(AuditLogData auditLogData);
|
||||
Task<PaginatedLogsResultDto<AuditRecordDto>> GetAuditLogsBySpecificFilterAsync(
|
||||
SpecificAuditLogFilterDto filterDto);
|
||||
|
||||
Task<PaginatedLogsResultDto<AuditRecordDto>> GetAuditLogsMatchingTextOrDateAsync(
|
||||
AuditLogTextOrDateSearchDTO filtertTextOrDateDto);
|
||||
|
||||
Task<T> DeepCopyAsync<T>(T source);
|
||||
Task<T> JsonDeepCopyAsync<T>(T source);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace audit_logs.Services.Interfaces;
|
||||
|
||||
public interface IDeepCopyService
|
||||
{
|
||||
Task<T> DeepCopyAsync<T>(T source);
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user