add girIgnore

This commit is contained in:
jrojas
2026-06-23 19:03:17 +02:00
parent 52335cc5fa
commit 95c9039c78
321 changed files with 84 additions and 12748 deletions
+29
View File
@@ -0,0 +1,29 @@
using audit_logs.Models;
using audit.Model;
namespace audit_logs.Utils;
public static class AuditRecordMapper
{
public static AuditRecordDto ToDto(this AuditRecord record)
{
return new AuditRecordDto
{
Id = record.Id.ToString(),
EntityType = record.EntityType,
RecordId = record.RecordId,
UserId = record.UserId,
ActionType = record.ActionType,
ActionTime = record.ActionTime,
Reason = record.Reason,
UserIpAddress = record.UserIpAddress,
Changes = record.Changes?.Select(c => new ChangeDto
{
Field = c.Field,
OldValue = AuditRecord.Change.ConvertBsonValue(c.OldValue),
NewValue = AuditRecord.Change.ConvertBsonValue(c.NewValue),
ValueType = c.ValueType
}).ToList() ?? new List<ChangeDto>()
};
}
}
+48
View File
@@ -0,0 +1,48 @@
using Newtonsoft.Json.Linq;
namespace audit_logs.Utils;
public static class JsonClean
{
public static JObject CleanJObject(JObject obj)
{
var properties = obj.Properties().ToList();
foreach (var property in properties)
{
if (property.Value.Type == JTokenType.Null)
{
// Eliminar la propiedad si el valor es null
property.Remove();
}
else if (property.Value.Type == JTokenType.Object)
{
// Limpieza para objetos anidados
property.Value = CleanJObject((JObject)property.Value);
}
else if (property.Value.Type == JTokenType.Array)
{
// Limpieza para arrays, aplicando limpieza a cada objeto dentro del array
property.Value = CleanJArray((JArray)property.Value);
}
}
return obj;
}
public static JArray CleanJArray(JArray array)
{
var cleanedArray = new JArray();
foreach (var item in array)
{
if (item.Type == JTokenType.Object)
{
cleanedArray.Add(CleanJObject((JObject)item));
}
else if (item.Type != JTokenType.Null)
{
cleanedArray.Add(item);
}
}
return cleanedArray;
}
}
+197
View File
@@ -0,0 +1,197 @@
using audit.Model;
namespace audit_logs.Utils;
using System;
using System.Collections.Generic;
using MongoDB.Bson;
using Newtonsoft.Json.Linq;
using Utils;
public class JsonComparer
{
public List<AuditRecord.Change> GetDifferences(object? originalJson, object? modifiedJson)
{
var changes = new List<AuditRecord.Change>();
// Convertir los objetos a cadenas JSON; usar "{}" si el objeto es null
var originalJsonString = originalJson != null
? JsonConverter.ConvertObjectToJson(originalJson)
: "{}";
var modifiedJsonString = modifiedJson != null
? JsonConverter.ConvertObjectToJson(modifiedJson)
: "{}";
// Intentar parsear a JObject solo si el JSON es un objeto
var originalObj = ParseAsJObject(originalJsonString);
var modifiedObj = ParseAsJObject(modifiedJsonString);
//eliminamos aquellas claves que contengan null, por ejemplo "allergies": [
// {
// "optionType": null,
// "name": "Sí",
// "iconDefault": "icCheck",
// "iconLight": null,
// "iconDark": null,
// "color": null,
// "bgColor": null,
// "description": null,
// "initDate": null,
// "endDate": null
// }
// ]
if (originalObj != null)
{
originalObj = JsonClean.CleanJObject(originalObj);
}
if (modifiedObj != null)
{
modifiedObj = JsonClean.CleanJObject(modifiedObj);
}
// Comparar los objetos JSON
CompareJTokens(originalObj, modifiedObj, changes, string.Empty);
return changes;
}
/// <summary>
/// Intenta parsear una cadena JSON como JObject. Devuelve null si no es un objeto.
/// </summary>
/// <param name="json">La cadena JSON a validar.</param>
/// <returns>Un JObject si es válido, de lo contrario null.</returns>
private static JObject? ParseAsJObject(string json)
{
try
{
var token = JToken.Parse(json);
return token as JObject; // Devuelve null si no es un JObject
}
catch
{
return null; // Si ocurre un error, no es un JSON válido
}
}
private void CompareJTokens(JToken original, JToken modified, List<AuditRecord.Change> changes, string path)
{
foreach (var property in modified.Children<JProperty>())
{
var fieldPath = string.IsNullOrEmpty(path) ? property.Name : $"{path}.{property.Name}";
var originalValue = original[property.Name];
var modifiedValue = property.Value;
// Ignorar campos donde ambos valores son null
if (originalValue == null && modifiedValue.Type == JTokenType.Null)
{
continue;
}
if (originalValue == null)
{
// Campo nuevo
changes.Add(new AuditRecord.Change
{
Field = fieldPath,
OldValue = BsonNull.Value,
NewValue = ConvertToBsonValue(modifiedValue),
ValueType = GetBsonType(modifiedValue)
});
}
else if (!JToken.DeepEquals(originalValue, modifiedValue))
{
if (originalValue.Type == JTokenType.Object && modifiedValue.Type == JTokenType.Object)
{
// Comparar objetos anidados
CompareJTokens(originalValue, modifiedValue, changes, fieldPath);
}
else
{
// Campo modificado
changes.Add(new AuditRecord.Change
{
Field = fieldPath,
OldValue = ConvertToBsonValue(originalValue),
NewValue = ConvertToBsonValue(modifiedValue),
ValueType = GetBsonType(modifiedValue)
});
}
}
}
// Campos eliminados
foreach (var property in original.Children<JProperty>())
{
var fieldPath = string.IsNullOrEmpty(path) ? property.Name : $"{path}.{property.Name}";
if (modified[property.Name] == null)
{
changes.Add(new AuditRecord.Change
{
Field = fieldPath,
OldValue = ConvertToBsonValue(property.Value),
NewValue = BsonNull.Value,
ValueType = GetBsonType(property.Value)
});
}
}
}
/*private BsonValue ConvertToBsonValue(JToken token)
{
return token.Type switch
{
JTokenType.String => new BsonString(token.ToString()),
JTokenType.Integer => new BsonInt32(token.ToObject<int>()),
JTokenType.Float => new BsonDouble(token.ToObject<double>()),
JTokenType.Boolean => new BsonBoolean(token.ToObject<bool>()),
JTokenType.Date => new BsonDateTime(token.ToObject<DateTime>()),
JTokenType.Null => BsonNull.Value,
JTokenType.Object => BsonDocument.Parse(token.ToString()),
JTokenType.Array => new BsonArray(token.ToObject<List<BsonValue>>()),
_ => new BsonString(token.ToString())
};
}*/
private BsonValue ConvertToBsonValue(JToken token)
{
switch (token.Type)
{
case JTokenType.String:
return new BsonString(token.ToString());
case JTokenType.Integer:
return BsonValue.Create(token.ToObject<int>());
case JTokenType.Float:
return BsonValue.Create(token.ToObject<double>());
case JTokenType.Boolean:
return new BsonBoolean(token.ToObject<bool>());
case JTokenType.Date:
return new BsonDateTime(token.ToObject<DateTime>());
case JTokenType.Null:
return BsonNull.Value;
case JTokenType.Object:
return BsonDocument.Parse(token.ToString());
case JTokenType.Array:
var array = new BsonArray();
foreach (var item in token.Children())
{
array.Add(ConvertToBsonValue(item));
}
return array;
default:
return new BsonString(token.ToString()); // Trata cualquier otro tipo como string
}
}
private string GetBsonType(JToken token)
{
return token.Type switch
{
JTokenType.String => "string",
JTokenType.Integer => "int",
JTokenType.Float => "double",
JTokenType.Boolean => "bool",
JTokenType.Date => "date",
JTokenType.Null => "null",
JTokenType.Object => "object",
JTokenType.Array => "array",
_ => "unknown"
};
}
}
+50
View File
@@ -0,0 +1,50 @@
using Newtonsoft.Json.Linq;
namespace audit_logs.Utils;
using System.Text.Json;
public static class JsonConverter
{
/// <summary>
/// Converts an object to a JSON string.
/// </summary>
/// <param name="obj">the object to be converted</param>
/// <returns>A JSON string representing the object</returns>
public static string ConvertObjectToJson(object obj)
{
try
{
// Si el objeto es una cadena, asumimos que ya es JSON
if (obj is string jsonString && IsValidJson(jsonString))
{
return jsonString; // No volver a serializar
}
var options = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var json = JsonSerializer.Serialize(obj);
return json;
}
catch (Exception ex)
{
return $"Error converting object to JSON: {ex.Message}";
}
}
/// <summary>
/// Valida si una cadena es un JSON válido.
/// </summary>
private static bool IsValidJson(string jsonString)
{
try
{
JToken.Parse(jsonString);
return true;
}
catch
{
return false;
}
}
}
+71
View File
@@ -0,0 +1,71 @@
using audit_logs.Models.AppSettings;
using MongoDB.Driver;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Bson.Serialization.Options;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.Driver;
namespace audit_logs.Utils;
public static class MongoDbHostBuilderExtension
{
#region MongoDB
private static MongoDB.Driver.MongoClient? _mongoClient;
private static IMongoDatabase? _mongoDb;
#endregion
public static IHostBuilder UseMongo(this IHostBuilder hostBuilder)
{
ConfigureMongoDbConventions();
// ConfigureRegisterMapClass();
return hostBuilder;
}
public static IMongoDatabase GetMongoDB(IOptions<MongoDbSettingsAudit> dbSettings)
{
_mongoDb ??= ConfigureMongoDbConnection(dbSettings);
return _mongoDb;
}
private static IMongoDatabase ConfigureMongoDbConnection(IOptions<MongoDbSettingsAudit> dbSettings)
{
var connectionString = dbSettings.Value.ConnectionString;
var databaseName = dbSettings.Value.DatabaseName;
if (string.IsNullOrEmpty(connectionString) || string.IsNullOrEmpty(databaseName))
{
throw new Exception("DataBase connection string and name is requiered");
}
_mongoClient = new MongoDB.Driver.MongoClient(connectionString);
var mongoDb = _mongoClient.GetDatabase(databaseName);
return mongoDb;
}
public static void ConfigureMongoDbConventions()
{
var _pack = new ConventionPack
{
new IgnoreExtraElementsConvention(true),
new CamelCaseElementNameConvention()
};
ConventionRegistry.Register(
"Ignore Extra Elements Convention",
_pack,
t => true);
ConventionRegistry.Register(
"Camel Case Convention",
_pack,
t => true);
}
}