fix(template-engine): resolve TemplateEngine-011,013,014 — remove dead converter, duplicate-id-safe cycle detection, unified deletion logic; TemplateEngine-012 deferred
This commit is contained in:
@@ -112,59 +112,18 @@ public class TemplateService
|
||||
if (template == null)
|
||||
return Result<bool>.Failure($"Template with ID {templateId} not found.");
|
||||
|
||||
// Derived templates are owned by their composition row and must be removed
|
||||
// by deleting the composition (which cascades) — block direct deletion.
|
||||
if (template.IsDerived)
|
||||
return Result<bool>.Failure(
|
||||
$"Cannot delete template '{template.Name}': it is a derived template. " +
|
||||
"Remove the owning composition on its parent template instead.");
|
||||
// Deletion-constraint logic (instances / child / derived / composing
|
||||
// templates) lives in exactly one place — TemplateDeletionService — so a
|
||||
// future rule change cannot drift between two implementations
|
||||
// (TemplateEngine-014). TemplateService owns only the audit-logging side
|
||||
// effect, which the deletion service is unaware of.
|
||||
var deletionService = new Services.TemplateDeletionService(_repository);
|
||||
var deleteResult = await deletionService.DeleteTemplateAsync(templateId, cancellationToken);
|
||||
if (deleteResult.IsFailure)
|
||||
return deleteResult;
|
||||
|
||||
// Check for instances referencing this template
|
||||
var instances = await _repository.GetInstancesByTemplateIdAsync(templateId, cancellationToken);
|
||||
if (instances.Count > 0)
|
||||
return Result<bool>.Failure(
|
||||
$"Cannot delete template '{template.Name}': it is referenced by {instances.Count} instance(s).");
|
||||
|
||||
// Check for child templates inheriting from this template.
|
||||
// Split derived vs. regular children — the message and remediation differ.
|
||||
var allTemplates = await _repository.GetAllTemplatesAsync(cancellationToken);
|
||||
var inheritors = allTemplates.Where(t => t.ParentTemplateId == templateId).ToList();
|
||||
var derivatives = inheritors.Where(t => t.IsDerived).ToList();
|
||||
var regularChildren = inheritors.Where(t => !t.IsDerived).ToList();
|
||||
|
||||
if (regularChildren.Count > 0)
|
||||
return Result<bool>.Failure(
|
||||
$"Cannot delete template '{template.Name}': it is inherited by {regularChildren.Count} child template(s): " +
|
||||
string.Join(", ", regularChildren.Select(c => $"'{c.Name}'")));
|
||||
|
||||
if (derivatives.Count > 0)
|
||||
{
|
||||
// Name each derivative by its owning parent template + composition slot.
|
||||
var ownerCompIds = derivatives.Select(d => d.OwnerCompositionId).Where(id => id.HasValue).Select(id => id!.Value).ToHashSet();
|
||||
var ownerLookup = allTemplates
|
||||
.SelectMany(t => t.Compositions.Select(c => new { Owner = t, Composition = c }))
|
||||
.Where(x => ownerCompIds.Contains(x.Composition.Id))
|
||||
.ToDictionary(x => x.Composition.Id, x => $"'{x.Owner.Name}' (as '{x.Composition.InstanceName}')");
|
||||
|
||||
var details = derivatives
|
||||
.Select(d => d.OwnerCompositionId.HasValue && ownerLookup.TryGetValue(d.OwnerCompositionId.Value, out var label)
|
||||
? label
|
||||
: $"'{d.Name}'");
|
||||
return Result<bool>.Failure(
|
||||
$"Cannot delete template '{template.Name}': it is the base of {derivatives.Count} derived template(s) used in: " +
|
||||
string.Join(", ", details) + ". Remove those compositions first.");
|
||||
}
|
||||
|
||||
// Check for templates composing this template
|
||||
var composedBy = allTemplates
|
||||
.Where(t => t.Compositions.Any(c => c.ComposedTemplateId == templateId))
|
||||
.ToList();
|
||||
if (composedBy.Count > 0)
|
||||
return Result<bool>.Failure(
|
||||
$"Cannot delete template '{template.Name}': it is composed by {composedBy.Count} template(s): " +
|
||||
string.Join(", ", composedBy.Select(c => $"'{c.Name}'")));
|
||||
|
||||
await _repository.DeleteTemplateAsync(templateId, cancellationToken);
|
||||
// TemplateDeletionService already persisted the delete; the audit entry
|
||||
// is added to the change tracker here and needs its own SaveChangesAsync.
|
||||
await _auditService.LogAsync(user, "Delete", "Template", templateId.ToString(), template.Name, null, cancellationToken);
|
||||
await _repository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user