318 lines
13 KiB
C#
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;
|
|
}
|
|
} |