122 lines
5.8 KiB
C#
122 lines
5.8 KiB
C#
using ScadaLink.Commons.Entities.Templates;
|
|
using ScadaLink.Commons.Interfaces.Repositories;
|
|
using ScadaLink.Commons.Interfaces.Services;
|
|
using ScadaLink.Commons.Types;
|
|
|
|
namespace ScadaLink.TemplateEngine.Services;
|
|
|
|
public class TemplateFolderService
|
|
{
|
|
private readonly ITemplateEngineRepository _repository;
|
|
private readonly IAuditService _auditService;
|
|
|
|
public TemplateFolderService(ITemplateEngineRepository repository, IAuditService auditService)
|
|
{
|
|
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
|
|
_auditService = auditService ?? throw new ArgumentNullException(nameof(auditService));
|
|
}
|
|
|
|
public async Task<Result<TemplateFolder>> CreateFolderAsync(
|
|
string name, int? parentFolderId, string user,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
return Result<TemplateFolder>.Failure("Folder name is required.");
|
|
|
|
if (parentFolderId.HasValue)
|
|
{
|
|
var parent = await _repository.GetFolderByIdAsync(parentFolderId.Value, cancellationToken);
|
|
if (parent == null)
|
|
return Result<TemplateFolder>.Failure($"Parent folder with ID {parentFolderId.Value} not found.");
|
|
}
|
|
|
|
var all = await _repository.GetAllFoldersAsync(cancellationToken);
|
|
if (all.Any(f => f.ParentFolderId == parentFolderId
|
|
&& string.Equals(f.Name, name, StringComparison.OrdinalIgnoreCase)))
|
|
return Result<TemplateFolder>.Failure($"A folder named '{name}' already exists at this level.");
|
|
|
|
var folder = new TemplateFolder(name) { ParentFolderId = parentFolderId };
|
|
await _repository.AddFolderAsync(folder, cancellationToken);
|
|
await _repository.SaveChangesAsync(cancellationToken);
|
|
await _auditService.LogAsync(user, "Create", "TemplateFolder", folder.Id.ToString(), name, folder, cancellationToken);
|
|
|
|
return Result<TemplateFolder>.Success(folder);
|
|
}
|
|
|
|
public async Task<Result<TemplateFolder>> RenameFolderAsync(
|
|
int folderId, string newName, string user,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(newName))
|
|
return Result<TemplateFolder>.Failure("Folder name is required.");
|
|
|
|
var folder = await _repository.GetFolderByIdAsync(folderId, cancellationToken);
|
|
if (folder == null)
|
|
return Result<TemplateFolder>.Failure($"Folder with ID {folderId} not found.");
|
|
|
|
var all = await _repository.GetAllFoldersAsync(cancellationToken);
|
|
if (all.Any(f => f.Id != folderId
|
|
&& f.ParentFolderId == folder.ParentFolderId
|
|
&& string.Equals(f.Name, newName, StringComparison.OrdinalIgnoreCase)))
|
|
return Result<TemplateFolder>.Failure($"A folder named '{newName}' already exists at this level.");
|
|
|
|
folder.Name = newName;
|
|
await _repository.UpdateFolderAsync(folder, cancellationToken);
|
|
await _repository.SaveChangesAsync(cancellationToken);
|
|
await _auditService.LogAsync(user, "Update", "TemplateFolder", folder.Id.ToString(), newName, folder, cancellationToken);
|
|
|
|
return Result<TemplateFolder>.Success(folder);
|
|
}
|
|
|
|
public async Task<Result<TemplateFolder>> MoveFolderAsync(
|
|
int folderId, int? newParentId, string user,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var folder = await _repository.GetFolderByIdAsync(folderId, cancellationToken);
|
|
if (folder == null)
|
|
return Result<TemplateFolder>.Failure($"Folder with ID {folderId} not found.");
|
|
|
|
if (newParentId.HasValue)
|
|
{
|
|
if (newParentId.Value == folderId)
|
|
return Result<TemplateFolder>.Failure("Cannot move a folder into itself (cycle).");
|
|
|
|
var newParent = await _repository.GetFolderByIdAsync(newParentId.Value, cancellationToken);
|
|
if (newParent == null)
|
|
return Result<TemplateFolder>.Failure($"Target folder with ID {newParentId.Value} not found.");
|
|
|
|
var all = await _repository.GetAllFoldersAsync(cancellationToken);
|
|
// Walk up from newParentId — if we encounter folderId, the move would create a cycle.
|
|
var byId = all.ToDictionary(f => f.Id);
|
|
var cursor = newParentId;
|
|
while (cursor.HasValue)
|
|
{
|
|
if (cursor.Value == folderId)
|
|
return Result<TemplateFolder>.Failure("Cannot move a folder under one of its descendants (cycle).");
|
|
cursor = byId.TryGetValue(cursor.Value, out var node) ? node.ParentFolderId : null;
|
|
}
|
|
|
|
// Sibling-name uniqueness in destination.
|
|
if (all.Any(f => f.Id != folderId
|
|
&& f.ParentFolderId == newParentId
|
|
&& string.Equals(f.Name, folder.Name, StringComparison.OrdinalIgnoreCase)))
|
|
return Result<TemplateFolder>.Failure($"A folder named '{folder.Name}' already exists in the target folder.");
|
|
}
|
|
else
|
|
{
|
|
var all = await _repository.GetAllFoldersAsync(cancellationToken);
|
|
if (all.Any(f => f.Id != folderId
|
|
&& f.ParentFolderId == null
|
|
&& string.Equals(f.Name, folder.Name, StringComparison.OrdinalIgnoreCase)))
|
|
return Result<TemplateFolder>.Failure($"A folder named '{folder.Name}' already exists at the root.");
|
|
}
|
|
|
|
folder.ParentFolderId = newParentId;
|
|
await _repository.UpdateFolderAsync(folder, cancellationToken);
|
|
await _repository.SaveChangesAsync(cancellationToken);
|
|
await _auditService.LogAsync(user, "Move", "TemplateFolder", folder.Id.ToString(), folder.Name, folder, cancellationToken);
|
|
|
|
return Result<TemplateFolder>.Success(folder);
|
|
}
|
|
}
|