feat(templates): lock ParentTemplateId after creation
Template inheritance is set once at create time and immutable on update. UpdateTemplateAsync now returns "Parent template cannot be changed after creation." when the caller sends a parent that differs from the stored value — server-side enforcement covers UI, ManagementService, and CLI. TemplateEdit renders the parent as static plaintext rather than an editable dropdown; TemplateCreate's parent picker is unchanged.
This commit is contained in:
@@ -83,28 +83,16 @@ public class TemplateService
|
||||
if (template == null)
|
||||
return Result<Template>.Failure($"Template with ID {templateId} not found.");
|
||||
|
||||
// Validate parent change
|
||||
if (parentTemplateId.HasValue && parentTemplateId.Value != (template.ParentTemplateId ?? 0))
|
||||
// ParentTemplateId is immutable after creation — set once at create time.
|
||||
// Reject any attempt to change it (null→value, value→null, or value→other).
|
||||
if (parentTemplateId != template.ParentTemplateId)
|
||||
{
|
||||
var parent = await _repository.GetTemplateByIdAsync(parentTemplateId.Value, cancellationToken);
|
||||
if (parent == null)
|
||||
return Result<Template>.Failure($"Parent template with ID {parentTemplateId.Value} not found.");
|
||||
|
||||
// Check inheritance acyclicity
|
||||
var allTemplates = await _repository.GetAllTemplatesAsync(cancellationToken);
|
||||
var cycleError = CycleDetector.DetectInheritanceCycle(templateId, parentTemplateId.Value, allTemplates);
|
||||
if (cycleError != null)
|
||||
return Result<Template>.Failure(cycleError);
|
||||
|
||||
// Check cross-graph cycle
|
||||
var crossCycleError = CycleDetector.DetectCrossGraphCycle(templateId, parentTemplateId, null, allTemplates);
|
||||
if (crossCycleError != null)
|
||||
return Result<Template>.Failure(crossCycleError);
|
||||
return Result<Template>.Failure(
|
||||
"Parent template cannot be changed after creation.");
|
||||
}
|
||||
|
||||
template.Name = name;
|
||||
template.Description = description;
|
||||
template.ParentTemplateId = parentTemplateId;
|
||||
|
||||
// Check for naming collisions after the change
|
||||
var collisionResult = await ValidateCollisionsAsync(template, cancellationToken);
|
||||
|
||||
Reference in New Issue
Block a user