N°9831 - Fix setup error when trying to remove class with a DEL_MANUAL ext. key#980
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes setup cleanup planning for nullable and mandatory DEL_MANUAL external keys.
Changes:
- Handles nullable external keys before
DEL_MANUAL. - Preserves accumulated deletion-plan entities.
- Adds order-independence regression tests.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
DataCleanupService.php |
Corrects nullable-key cleanup behavior. |
StaticDeletionPlan.php |
Prevents accumulated issues from being overwritten. |
StaticDeletionPlanTest.php |
Tests both class-processing orders. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
| Filename | Overview |
|---|---|
| datamodels/2.x/combodo-data-feature-removal/src/Service/DataCleanupService.php | Handles nullable external keys before applying DEL_MANUAL blocking behavior. |
| datamodels/2.x/combodo-data-feature-removal/src/Service/StaticDeletionPlan.php | Reuses existing plan entities so earlier issues and updates are not overwritten. |
| tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/StaticDeletionPlanTest.php | Adds tests for both processing orders and removes an unused import. |
Reviews (1): Last reviewed commit: "N°9831 - Fix ext. management deletion pl..." | Re-trigger Greptile
…way as \Combodo\iTop\DataFeatureRemoval\Service\StaticDeletionPlan::DeletionPlanForReferencingClasses and \DBObject::MakeDeletionPlan - Check if ext. key is nullable first - Then check if its a DEL_MANUAL option
920f86d to
2f437bd
Compare
| if (false === isset($this->aDeletionPlan[$sClass])) { | ||
| $this->aDeletionPlan[$sClass] = new DeletionPlanEntity(); |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
datamodels/2.x/combodo-data-feature-removal/src/Service/StaticDeletionPlan.php:66
- This now preserves the plan across separate calls on the same service instance, not only across classes in one call. For example, after
GetStaticDeletionPlan(['DFRManual']), a later call after those rows were removed still returns the old delete IDs/issues becauseMerge()never removes them. Reset the per-computation state before the loop, while retaining the conditional creation here so entities are still preserved within one computation.
if (false === array_key_exists($sClass, $this->aDeletionPlan)) {
$this->aDeletionPlan[$sClass] = new DeletionPlanEntity();
}
$this->aDeletionPlan[$sClass]->oDelete->Merge($oDeletionPlanItem);
…riting entity for a previously processed class
c7d815e to
0770d89
Compare
Base information
Please be advised that I tried to make a fix for both bugs but that I don't master these parts of the code, so I may not be aware of the big picture.
Symptom (bug) / Objective (enhancement)
When uninstalling a module through the setup, the "check compatibility" step detects the objects that must be cleaned up to keep iTop consistent and redirects to the "extension management" page, which displays a summary of what will be deleted/updated. When some of these objects can't be deleted automatically (e.g. an external key with
DEL_MANUALoption), the page is supposed to show a blocking message telling the user that it must remove these data manually first, and the "Proceed with deletion" button must not lead to a deletion.But in this case, for some modules the blocking message isn't shown even though objects cannot be deleted automatically. The "Proceed with deletion" button stays enabled, and clicking it crashes the application with:
Concrete cases found while troubleshooting (standard datamodel):
is_null_allowedon_target_deleteEnclosure.rack_id=>RackfalseDEL_MANUALHypervisor.farm_id=>FarmtrueDEL_MANUALVirtualMachine.virtualhost_id=>VirtualHost(abstract class)falseDEL_MANUALReproduction procedure (bug)
Enclosurelinked to aRackVirtualizationCause (bug)
The "summary" (whether to block) and the "actual execution" rely on two different deletion-plan services that do not do the same processing:
StaticDeletionPlan::GetCleanupSummary()(called byDataFeatureRemovalController::GetDeletionPlanSummaryTable())DataCleanupService::RecursiveDeletion(), which throws the exception throughObjectService::SetIssue().Two different bugs made them diverge:
Cause of bug #1 - Wrong order between
IsNullAllowed()andDEL_MANUAL(DataCleanupService::RecursiveDeletion())The "execution" service tested
DEL_MANUALbeforeIsNullAllowed(), so a nullable external key withDEL_MANUALwas treated as an unresolvable issue and threw an exception.But the reference engine
DBObject::MakeDeletionPlan()(andStaticDeletionPlan) testIsNullAllowed()first.A nullable key is always a simple "reset to null" update, and
DEL_MANUALonly matters for mandatory ext. keys. That caused forHypervisor.farm_id(nullable +DEL_MANUAL) the "summary" reported an update (no issue, no blocking message) while "execution" threw an exception.Cause of bug #2 - Deletion-plan entity overwritten (
StaticDeletionPlan::GetStaticDeletionPlan())While iterating the classes to remove, the method created a new
DeletionPlanEntityfor each classes and assigned it, loosing any entity already populated for that class by a previously processed class (issues/updates are accumulated on the referencing class while its target is processed).The setup audit feeds the classes sorted alphabetically and skips abstract classes (
AbstractSetupAudit::RunDataAudit()). ForVirtualMachine.virtualhost_id=>VirtualHost:VirtualHostis abstract, so only the concrete subclassesCloud/Farm/Hypervisorare listed;VirtualMachine(referencing, mandatoryDEL_MANUAL) is sorted after them, so the issue recorded on theVirtualMachineentity while processingFarm/Hypervisor/Cloudwas lost whenVirtualMachinewas later processed as a root class =>iIssueCount = 0=> no blocking message, but execution still detected it and threw an exception.This also explains why the
Data center devicesmodule works as expected:Enclosure(referencing) is sorted beforeRack(its target), so the issue recorded onEnclosureis never overwritten afterwards.Proposed solution (bug and enhancement)
Fix for bug #1
In the "summary" service, test
IsNullAllowed()first like in the "execution" service (StaticDeletionPlan) and coreDBObject::MakeDeletionPlan().Fix for bug #2
Do not overwrite an existing plan entity, create it only when missing, then merge the initial IDs.
Issues/updates already accumulated are preserved, making issue detection independent of the processing order.
Tests
I've tried to add test cases in
StaticDeletionPlanTestbut I really not sure how it's supposed to work, so reviews are welcome 😅Checklist before requesting a review