diff --git a/datamodels/2.x/combodo-data-feature-removal/src/Service/DeletionPlanService.php b/datamodels/2.x/combodo-data-feature-removal/src/Service/DeletionPlanService.php index c0cce332b1..91fbdac017 100644 --- a/datamodels/2.x/combodo-data-feature-removal/src/Service/DeletionPlanService.php +++ b/datamodels/2.x/combodo-data-feature-removal/src/Service/DeletionPlanService.php @@ -89,7 +89,9 @@ class DeletionPlanService } /** + * @since 3.3.0 * @param array $aClasses + * @param int $iUnixTimeLimit : max execution time in seconds since Epoch before stopping deletion. by default: no limit (ie remove all without stop) * * @return array<\Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanSummaryEntity> * @throws \Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException @@ -97,7 +99,7 @@ class DeletionPlanService * @throws \CoreUnexpectedValue * @throws \MySQLException */ - public function ExecuteDeletionPlan(array $aClasses): array + public function ExecuteDeletionPlan(array $aClasses, int $iUnixTimeLimit = 0): array { $oDeletionPlan = $this->GetDeletionPlan($aClasses); @@ -110,6 +112,11 @@ class DeletionPlanService $oDeletionPlanSummaryEntity = $aSummary[$sClass] ?? new DeletionPlanSummaryEntity($sClass); foreach ($aToUpdate as $aData) { + if ($this->IsTimeLimitExceeded($iUnixTimeLimit)) { + $aSummary[$sClass] = $oDeletionPlanSummaryEntity; + return $aSummary; + } + $oToUpdate = $aData['to_reset']; /** @var \DBObject $oToUpdate */ foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef) { @@ -126,6 +133,11 @@ class DeletionPlanService $oDeletionPlanSummaryEntity = $aSummary[$sClass] ?? new DeletionPlanSummaryEntity($sClass); foreach ($aDeletes as $sId => $aDelete) { + if ($this->IsTimeLimitExceeded($iUnixTimeLimit)) { + $aSummary[$sClass] = $oDeletionPlanSummaryEntity; + return $aSummary; + } + try { CMDBSource::Query('START TRANSACTION'); // Delete any existing change tracking about the current object @@ -144,7 +156,7 @@ class DeletionPlanService CMDBSource::Query('COMMIT'); } catch (\Exception $e) { - \IssueLog::Exception(__METHOD__.': Cleanup failed', $e); + \IssueLog::Exception(__METHOD__.": Cleanup failed", $e); CMDBSource::Query('ROLLBACK'); throw $e; } @@ -179,4 +191,13 @@ class DeletionPlanService return $oDeletionPlan; } + + public function IsTimeLimitExceeded(int $iUnixTimeLimit): bool + { + if ($iUnixTimeLimit === 0) { + return false; + } + + return (time() <= $iUnixTimeLimit); + } } diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/DeletionPlanServiceTest.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/DeletionPlanServiceTest.php index 4d78905208..3ee6bc756c 100644 --- a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/DeletionPlanServiceTest.php +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/DeletionPlanServiceTest.php @@ -10,7 +10,7 @@ namespace Combodo\iTop\Test\UnitTest\Module\DataFeatureRemoval\Service; use Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanSummaryEntity; use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalException; use Combodo\iTop\DataFeatureRemoval\Service\DeletionPlanService; -use Combodo\iTop\Test\UnitTest\ItopDataTestCase; +use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase; use DeletionPlan; /** @@ -32,7 +32,7 @@ use DeletionPlan; * @see DeletionPlanSummaryEntity * @see ItopDataTestCase */ -class DeletionPlanServiceTest extends ItopDataTestCase +class DeletionPlanServiceTest extends ItopCustomDatamodelTestCase { protected function setUp(): void { @@ -274,4 +274,53 @@ class DeletionPlanServiceTest extends ItopDataTestCase $oMockService->ExecuteDeletionPlan(['SomeClass']); } + + public function testExecuteDeletionPlan_DeleteAllWithoutLimit() + { + $iDFRToRemoveLeaf = $this->GivenObjectInDB('DFRToRemoveLeaf', ['name' => 'ga']); + $this->GivenObjectInDB('DFRToUpdate', ['name' => 'bu', 'dfrtoremove_id' => $iDFRToRemoveLeaf]); + $iDFRRemovedCollateral = $this->GivenObjectInDB('DFRRemovedCollateral', ['name' => 'zo', 'dfrtoremove_id' => $iDFRToRemoveLeaf]); + $this->GivenObjectInDB('DFRRemovedCollateralCascade', ['name' => 'meu', 'dfrremovedcollateral_id' => $iDFRRemovedCollateral]); + + $aClasses = [ 'DFRToRemoveLeaf' ]; + $aRes = DeletionPlanService::GetInstance()->ExecuteDeletionPlan($aClasses); + $aExpected = [ + ['DFRToUpdate', 1, 0 ], + ['DFRToRemoveLeaf', 0, 1 ], + ['DFRRemovedCollateral', 0, 1 ], + ['DFRRemovedCollateralCascade', 0, 1 ], + ]; + $this->AssertSummaryEquals($aExpected, $aRes, 'DeleteAllWithoutLimit'); + } + + private function AssertSummaryEquals(array $expected, $actual, $sMessage = '') + { + $aExpected = []; + foreach ($expected as $line) { + $sClass = $line[0]; + $iUpdate = $line[1]; + $iDelete = $line[2]; + + $oDeletionPlanSummaryEntity = new DeletionPlanSummaryEntity($sClass); + $oDeletionPlanSummaryEntity->iUpdateCount = $iUpdate; + $oDeletionPlanSummaryEntity->iDeleteCount = $iDelete; + $aExpected[$sClass] = $oDeletionPlanSummaryEntity; + } + $this->assertEquals($aExpected, $actual, $sMessage); + } + + public function testExecuteDeletionPlan_StopInUpdates() + { + self::markTestSkipped(); + } + + public function testExecuteDeletionPlan_StopInDeletes() + { + self::markTestSkipped(); + } + + public function GetDatamodelDeltaAbsPath(): string + { + return __DIR__.'/deletionplan_delta.xml'; + } }