Backend/e-suite.Modules.GlossariesManager/e-suite.Modules.GlossariesManager/GlossariesManager.cs
2026-01-20 21:50:10 +00:00

318 lines
13 KiB
C#

using e_suite.API.Common;
using e_suite.API.Common.exceptions;
using e_suite.API.Common.models;
using e_suite.API.Common.repository;
using e_suite.Database.Audit;
using e_suite.Database.Core.Helpers;
using e_suite.Database.Core.Tables.Glossaries;
using eSuite.Core.Miscellaneous;
using e_suite.Database.Core.Tables.CustomFields;
namespace e_suite.Modules.GlossariesManager;
public class GlossariesManager : IGlossariesManager
{
private readonly IGlossariesManagerRepository _glossariesManagerRepository;
private readonly ICustomFieldValidator _customFieldValidator;
private readonly ICustomFieldHelper _customFieldHelper;
public GlossariesManager(IGlossariesManagerRepository glossariesManagerRepository, ICustomFieldValidator customFieldValidator, ICustomFieldHelper customFieldHelper)
{
_glossariesManagerRepository = glossariesManagerRepository;
_customFieldValidator = customFieldValidator;
_customFieldHelper = customFieldHelper;
}
public async Task<GlossaryItem?> GetGlossaryItem(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken)
{
var glossary = await _glossariesManagerRepository.FindGlossary(generalIdRef, cancellationToken) ??
throw new NotFoundException("Glossary not found");
await LoadParentHierarchy(glossary, cancellationToken);
var glossaryItem = await GlossaryItemHelper.TranslateToGlossaryItem(glossary, _customFieldHelper, cancellationToken);
if (glossaryItem == null)
return glossaryItem;
var children = await _glossariesManagerRepository.FindGlossaryChildren(glossaryItem.Id, cancellationToken);
var childGlossaryItems = new List<GlossaryItem>();
foreach (var child in children)
{
if (!child.Deleted)
{
childGlossaryItems.Add((await GlossaryItemHelper.TranslateToGlossaryItem(child, _customFieldHelper, cancellationToken))!);
}
}
glossaryItem.Children = childGlossaryItems;
return glossaryItem;
}
private async Task LoadParentHierarchy(Glossary glossary, CancellationToken cancellationToken)
{
var item = glossary;
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
while (item != null)
{
item.Parent ??= (await _glossariesManagerRepository.FindGlossary(new GeneralIdRef { Id = item.ParentId }, cancellationToken))!;
item = item.Parent;
}
}
public async Task AddGlossaryItem(AuditUserDetails auditUserDetails, NewGlossaryItem glossaryItem, CancellationToken cancellationToken)
{
await _glossariesManagerRepository.TransactionAsync(async () =>
{
await DoAddGlossaryItem(auditUserDetails, [glossaryItem], cancellationToken);
});
}
public async Task AddGlossaryItems(AuditUserDetails auditUserDetails, IEnumerable<NewGlossaryItem> glossaryItems, CancellationToken cancellationToken)
{
await _glossariesManagerRepository.TransactionAsync(async () =>
{
await DoAddGlossaryItem(auditUserDetails, glossaryItems, cancellationToken);
});
}
private async Task DoAddGlossaryItem(AuditUserDetails auditUserDetails, IEnumerable<NewGlossaryItem> glossaryItems,
CancellationToken cancellationToken)
{
var itemsToWorkOn = new List<(NewGlossaryItem, Glossary)>();
foreach (var glossaryItem in glossaryItems)
{
var parent =
await _glossariesManagerRepository.GetParentGlossary(glossaryItem.Parent, cancellationToken);
if (glossaryItem.Parent != null && parent == null)
throw new NullReferenceException("parent could not be found");
var duplicate = glossaryItem.Guid == null
? null
: await _glossariesManagerRepository.FindGlossary(new GeneralIdRef { Guid = glossaryItem.Guid },
cancellationToken);
if (duplicate != null)
throw new ArgumentException($"Guid {glossaryItem.Guid} is already used");
var glossary = new Glossary
{
Deleted = false,
Guid = glossaryItem.Guid ?? Guid.NewGuid(),
Name = glossaryItem.Name,
ParentId = parent?.Id,
Parent = parent!
};
itemsToWorkOn.Add(new ValueTuple<NewGlossaryItem, Glossary>
{
Item1 = glossaryItem,
Item2 = glossary
});
}
await _glossariesManagerRepository.AddGlossaryItems(auditUserDetails, itemsToWorkOn.Select(x => x.Item2),
cancellationToken);
await UpdateChildCustomFieldDefinitions(auditUserDetails,
itemsToWorkOn.Select(x =>
new ValueTuple<IEnumerable<IGeneralIdRef>, Glossary>
{
Item1 = x.Item1.ChildCustomFieldDefinition,
Item2 = x.Item2
}
),
cancellationToken);
await UpdateCustomFieldValues(auditUserDetails, itemsToWorkOn.Select(x =>
new ValueTuple<EditableGlossaryItem, Glossary>
{
Item1 = x.Item1,
Item2 = x.Item2
}
), cancellationToken);
}
public async Task UpdateGlossaryItem(AuditUserDetails auditUserDetails, EditGlossaryItem editGlossaryItem, CancellationToken cancellationToken)
{
await _glossariesManagerRepository.TransactionAsync(async () =>
{
var glossaryItem = await _glossariesManagerRepository.FindGlossary(editGlossaryItem.Id, cancellationToken) ??
throw new ArgumentNullException(nameof(editGlossaryItem),"Glossary item not found");
glossaryItem.Name = editGlossaryItem.Name;
await UpdateChildCustomFieldDefinitions(auditUserDetails, glossaryItem, editGlossaryItem.ChildCustomFieldDefinition, cancellationToken);
await UpdateCustomFieldValues(auditUserDetails, glossaryItem, editGlossaryItem, cancellationToken);
await _glossariesManagerRepository.EditGlossaryItem(auditUserDetails, glossaryItem, cancellationToken);
});
}
private async Task UpdateCustomFieldValues(AuditUserDetails auditUserDetails, IEnumerable<(EditableGlossaryItem, Glossary)> itemsToWorkOn,
CancellationToken cancellationToken)
{
List<Delta<GlossaryCustomFieldValue>> deltas = [];
foreach (var item in itemsToWorkOn)
{
if (item.Item2.Parent == null && item.Item1.CustomFieldValues.Count > 0)
throw new NullReferenceException("Root items cannot have custom field values.");
var updatedcustomFieldValues =
await CompileGlossaryCustomFieldValues(auditUserDetails, item.Item1, item.Item2.Parent, item.Item2,
cancellationToken);
var currentCustomFieldValues = await _glossariesManagerRepository.GetGlossaryCustomValues(item.Item2, cancellationToken);
var glossaryCustomFieldValueKeyComparer = new GlossaryCustomFieldValueKeyComparer();
Delta<GlossaryCustomFieldValue> delta = Delta<GlossaryCustomFieldValue>.CalculateDelta(updatedcustomFieldValues, currentCustomFieldValues, glossaryCustomFieldValueKeyComparer, cancellationToken);
Parallel.ForEach(delta.Matches, match =>
{
match.Original.DisplayValue = match.Updated.DisplayValue;
match.Original.Value = match.Updated.Value;
});
deltas.Add(delta);
}
await _glossariesManagerRepository.SaveCustomFieldValues(auditUserDetails, deltas, cancellationToken);
}
private async Task UpdateCustomFieldValues(AuditUserDetails auditUserDetails, Glossary glossaryItem,
EditableGlossaryItem glossary, CancellationToken cancellationToken)
{
var items = new List<(EditableGlossaryItem, Glossary)>
{
new()
{
Item1 = glossary,
Item2 = glossaryItem
}
};
await UpdateCustomFieldValues(auditUserDetails, items, cancellationToken);
}
private async Task UpdateChildCustomFieldDefinitions(AuditUserDetails auditUserDetails, Glossary glossary,
IEnumerable<IGeneralIdRef> customFieldIds, CancellationToken cancellationToken)
{
var itemsToWorkOn = new List<(IEnumerable<IGeneralIdRef>, Glossary)>
{
new()
{
Item1 = customFieldIds,
Item2 = glossary
}
};
await UpdateChildCustomFieldDefinitions(auditUserDetails,
itemsToWorkOn, cancellationToken);
}
private async Task UpdateChildCustomFieldDefinitions(AuditUserDetails auditUserDetails,
IEnumerable<(IEnumerable<IGeneralIdRef>, Glossary)> itemsToWorkOn, CancellationToken cancellationToken)
{
var removalList = new List<GlossaryCustomField>();
var additionList = new List<GlossaryCustomField>();
foreach (var item in itemsToWorkOn)
{
List<CustomField> customFieldDefinitions;
try
{
customFieldDefinitions =
await _glossariesManagerRepository.GetCustomFieldDefinitions(item.Item1, cancellationToken);
}
catch (NullReferenceException ex)
{
throw new NotFoundException("Unable to find custom field", ex);
}
foreach (var customField in _glossariesManagerRepository.GetGlossaryCustomFields(item.Item2.Id))
{
var found = false;
foreach (var a in customFieldDefinitions)
{
if (a.Id == customField.Id)
{
found = true;
break;
}
}
if (!found)
removalList.Add(customField);
}
foreach (var customFieldDefinition in customFieldDefinitions)
{
cancellationToken.ThrowIfCancellationRequested();
var glossaryCustomField = new GlossaryCustomField
{
GlossaryId = item.Item2.Id,
CustomFieldId = customFieldDefinition.Id
};
additionList.Add(glossaryCustomField);
}
}
await _glossariesManagerRepository.SaveChildCustomFieldDefinitions(auditUserDetails, removalList, additionList, cancellationToken);
}
private async Task<List<GlossaryCustomFieldValue>> CompileGlossaryCustomFieldValues(AuditUserDetails auditUserDetails, EditableGlossaryItem glossaryItem, Glossary? parent, Glossary glossary, CancellationToken cancellationToken)
{
if (parent == null)
return [];
var parentCustomFieldDefinitions = parent.CustomFieldDefinitions.Select(x => x.CustomField).ToList();
var inputFieldValues = glossaryItem.CustomFieldValues.ToCustomFieldsValues();
var validatedFields = await _customFieldValidator.ValidateFields(auditUserDetails, inputFieldValues, parentCustomFieldDefinitions, cancellationToken);
return validatedFields.Select(validatedField => new GlossaryCustomFieldValue
{
GlossaryId = glossary.Id,
CustomFieldId = validatedField.CustomFieldId,
Index = validatedField.Index,
Value = validatedField.Value,
DisplayValue = validatedField.DisplayValue!
})
.ToList();
}
public async Task DeleteGlossaryItem(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken)
{
await _glossariesManagerRepository.TransactionAsync(async () =>
{
var glossaryItem = await _glossariesManagerRepository.FindGlossary(generalIdRef, cancellationToken) ??
throw new NotFoundException("Unable to find glossary item");
if (glossaryItem!.Deleted)
throw new InvalidOperationException("Already deleted");
await SetItemAndChildrenAsDeleted(glossaryItem, cancellationToken);
await _glossariesManagerRepository.EditGlossaryItem(auditUserDetails, glossaryItem, cancellationToken);
});
}
private async Task SetItemAndChildrenAsDeleted(Glossary glossaryItem, CancellationToken cancellationToken)
{
var children = await _glossariesManagerRepository.FindGlossaryChildren(glossaryItem.Id, cancellationToken);
foreach (var child in children)
{
await SetItemAndChildrenAsDeleted(child, cancellationToken);
}
glossaryItem.Deleted = true;
}
}