From b0a55e057b41bf2cfba80eee6362ad781116e828 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Thu, 7 Apr 2022 17:02:19 +0200 Subject: [PATCH] Events to cmdbAbstract --- application/cmdbabstract.class.inc.php | 128 +++++++++- application/datamodel.application.xml | 216 ++++++++++++++++- core/datamodel.core.xml | 228 ------------------ core/dbobject.class.php | 84 ++++--- .../datamodel.itop-attachments.xml | 68 ++++++ setup/compiler.class.inc.php | 2 +- setup/setuputils.class.inc.php | 14 -- test/ItopDataTestCase.php | 49 +++- test/core/DBObjectTest.php | 45 ++++ 9 files changed, 554 insertions(+), 280 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 7157eff45..b64ccd5b3 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -2132,7 +2132,7 @@ HTML; $sDisplayValueForHtml = utils::EscapeHtml($sDisplayValue); $sHTMLValue = << - + {$sValidationSpan}{$sReloadSpan} HTML; break; @@ -5716,4 +5716,130 @@ JS 'AttributeOneWayPassword', ); } + + /** + * @return void + * @throws \CoreException + */ + final protected function EventInsertRequested() + { + $sClass = get_class($this); + if ($sClass == 'UserRequest') { + IssueLog::Debug("CRUD: DBInsert $sClass::0 Requested", LogChannels::DM_CRUD); + } + $this->FireEvent(EVENT_SERVICE_DB_INSERT_REQUESTED); + } + + /** + * @return void + * @throws \CoreException + */ + final protected function EventInsertBefore() + { + $sClass = get_class($this); + if ($sClass == 'UserRequest') { + IssueLog::Debug("CRUD: DBInsert $sClass::0 About to write in DB", LogChannels::DM_CRUD); + } + $this->FireEvent(EVENT_SERVICE_DB_BEFORE_INSERT); + } + + /** + * @return void + * @throws \CoreException + */ + final protected function EventInsertAfter() + { + $sClass = get_class($this); + if ($sClass == 'UserRequest') { + IssueLog::Debug("CRUD: $sClass::{$this->m_iKey} Inserted in DB", LogChannels::DM_CRUD); + } + + $this->FireEvent(EVENT_SERVICE_DB_AFTER_INSERT); + } + + /** + * @param array $aEventData + * + * @return void + * @throws \CoreException + */ + final protected function EventCheckToWrite(array $aEventData) + { + $this->FireEvent(EVENT_SERVICE_DB_CHECK_TO_WRITE, $aEventData); + } + + /** + * @param array $aEventData + * + * @return void + * @throws \CoreException + */ + final protected function EventCheckToDelete(array $aEventData) + { + $this->FireEvent(EVENT_SERVICE_DB_CHECK_TO_DELETE, $aEventData); + } + + /** + * @return void + * @throws \CoreException + */ + final protected function EventUpdateRequested() + { + $sClass = get_class($this); + $sKey = $sClass.'::'.$this->GetKey(); + if ($sClass == 'UserRequest') { + IssueLog::Debug("CRUD: DBUpdate $sKey Requested", LogChannels::DM_CRUD); + } + + $this->FireEvent(EVENT_SERVICE_DB_UPDATE_REQUESTED); + } + + /** + * @return void + * @throws \CoreException + */ + final protected function EventUpdateBefore() + { + $sClass = get_class($this); + $sKey = $sClass.'::'.$this->GetKey(); + if ($sClass == 'UserRequest') { + IssueLog::Debug("CRUD: DBUpdate $sKey About to be written in DB", LogChannels::DM_CRUD); + } + $this->FireEvent(EVENT_SERVICE_DB_BEFORE_UPDATE); + } + + /** + * @param array $aEventData + * + * @return void + * @throws \CoreException + */ + final protected function EventUpdateAfter(array $aEventData) + { + $sClass = get_class($this); + $sKey = $sClass.'::'.$this->GetKey(); + if ($sClass == 'UserRequest') { + IssueLog::Debug("CRUD: DBUpdate $sKey Updated", LogChannels::DM_CRUD); + } + $this->FireEvent(EVENT_SERVICE_DB_AFTER_UPDATE, $aEventData); + } + + /** + * @return void + * @throws \CoreException + */ + final protected function EventDeleteBefore() + { + $this->FireEvent(EVENT_SERVICE_DB_BEFORE_DELETE); + } + + /** + * @return void + * @throws \CoreException + */ + final protected function EventDeleteAfter() + { + $this->FireEvent(EVENT_SERVICE_DB_AFTER_DELETE); + } + } diff --git a/application/datamodel.application.xml b/application/datamodel.application.xml index 418736f0b..1fc532c58 100644 --- a/application/datamodel.application.xml +++ b/application/datamodel.application.xml @@ -186,7 +186,219 @@ - + + An object insert in the database has been requested. All changes to the object will be persisted automatically. + DBObject::OnInsert + + + The object inserted + DBObject + + + Debug string + string + + + + + An object is about to be inserted in the database (no change possible) + DBObject::OnInsert + + + The object inserted + DBObject + + + Debug string + string + + + + + An object has been inserted into the database (but not reloaded). All changes to the object will be persisted automatically. + DBObject::AfterInsert + + + The object inserted + DBObject + + + Debug string + string + + + + + An object update has been requested. All changes to the object will be persisted automatically. + DBObject::OnUpdate, DBObject::DoComputeValues + + + The object updated + DBObject + + + Debug string + string + + + + + An object is about to be updated in the database (no change possible) + DBObject::OnUpdate + + + The object updated + DBObject + + + Debug string + string + + + + + An object has been updated into the database and reloaded. All changes to the object will be persisted automatically. + DBObject::AfterUpdate + + + The object updated + DBObject + + + Debug string + string + + + + + An object is about to be deleted in the database + DBObject::OnDelete + + + The object deleted + DBObject + + + Debug string + string + + + + + An object has been deleted into the database + DBObject::AfterDelete + + + The object deleted + DBObject + + + Debug string + string + + + + + A stimulus is about to be applied to an object + + + The object where the stimulus is to be applied + DBObject + + + Current stimulus applied + string + + + Debug string + string + + + + + A stimulus has been applied to an object + + + The object where the stimulus has been applied + DBObject + + + Current stimulus applied + string + + + Debug string + string + + + + + An object has been re-loaded from the database + + + The object re-loaded + DBObject + + + Debug string + string + + + + + Check an object before it is written into the database (no change possible) + cmdbAbstractObject::DoCheckToWrite + + + The object to check + DBObject + + + Array of strings where all the errors found during the object checking are added + array + + + Debug string + string + + + + + Check an object before it is deleted from the database (no change possible) + cmdbAbstractObject::DoCheckToDelete + + + The object to check + DBObject + + + Array of strings where all the errors found during the object checking are added + array + + + Debug string + string + + + + + A document has been downloaded from the GUI + + + The object containing the document + DBObject + + + The document downloaded + ormDocument + + + Debug string + string + + + + The current page is completely displayed Class hierarchy of the displayed page @@ -200,7 +412,7 @@ - + Inform the listeners about the connection states diff --git a/core/datamodel.core.xml b/core/datamodel.core.xml index 95f6140ee..b24417e75 100644 --- a/core/datamodel.core.xml +++ b/core/datamodel.core.xml @@ -11,234 +11,6 @@ - - - An object insert in the database has been requested. All changes to the object will be persisted automatically. - DBObject::OnInsert - - - The object inserted - DBObject - - - Debug string - string - - - - - An object is about to be inserted in the database (no change possible) - DBObject::OnInsert - - - The object inserted - DBObject - - - Debug string - string - - - - - An object has been inserted into the database (but not reloaded). All changes to the object will be persisted automatically. - DBObject::AfterInsert - - - The object inserted - DBObject - - - Debug string - string - - - - - An object update has been requested. All changes to the object will be persisted automatically. - DBObject::OnUpdate, DBObject::DoComputeValues - - - The object updated - DBObject - - - Debug string - string - - - - - An object is about to be updated in the database (no change possible) - DBObject::OnUpdate - - - The object updated - DBObject - - - Debug string - string - - - - - An object has been updated into the database (but not reloaded). All changes to the object will be LOST. - DBObject::AfterUpdate - - - The object updated - DBObject - - - Debug string - string - - - - - An object has been updated into the database and reloaded. All changes to the object will be persisted automatically. - DBObject::AfterUpdate - - - The object updated - DBObject - - - Debug string - string - - - - - An object is about to be deleted in the database - DBObject::OnDelete - - - The object deleted - DBObject - - - Debug string - string - - - - - An object has been deleted into the database - DBObject::AfterDelete - - - The object deleted - DBObject - - - Debug string - string - - - - - A stimulus is about to be applied to an object - - - The object where the stimulus is to be applied - DBObject - - - Current stimulus applied - string - - - Debug string - string - - - - - A stimulus has been applied to an object - - - The object where the stimulus has been applied - DBObject - - - Current stimulus applied - string - - - Debug string - string - - - - - An object has been re-loaded from the database - - - The object re-loaded - DBObject - - - Debug string - string - - - - - Check an object before it is written into the database (no change possible) - cmdbAbstractObject::DoCheckToWrite - - - The object to check - DBObject - - - Array of strings where all the errors found during the object checking are added - array - - - Debug string - string - - - - - Check an object before it is deleted from the database (no change possible) - cmdbAbstractObject::DoCheckToDelete - - - The object to check - DBObject - - - Array of strings where all the errors found during the object checking are added - array - - - Debug string - string - - - - - A document has been downloaded from the GUI - - - The object containing the document - DBObject - - - The document downloaded - ormDocument - - - Debug string - string - - - - diff --git a/core/dbobject.class.php b/core/dbobject.class.php index ce3a35e0b..2fc3dd91b 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -150,6 +150,8 @@ abstract class DBObject implements iDisplay */ protected $m_sEventUniqId = ''; + private static $aUpdateReentrance = []; + /** * DBObject constructor. @@ -2341,7 +2343,7 @@ abstract class DBObject implements iDisplay $oKPI = new ExecutionKPI(); $this->DoCheckToWrite(); - $this->FireEvent(EVENT_SERVICE_DB_CHECK_TO_WRITE, array('error_messages' => &$this->m_aCheckIssues)); + $this->EventCheckToWrite(['error_messages' => &$this->m_aCheckIssues]); $oKPI->ComputeStats('CheckToWrite', get_class($this)); if (count($this->m_aCheckIssues) == 0) { @@ -2370,7 +2372,7 @@ abstract class DBObject implements iDisplay { $this->m_aDeleteIssues = array(); // Ok - $this->FireEvent(EVENT_SERVICE_DB_CHECK_TO_DELETE, array('error_messages' => &$this->m_aDeleteIssues)); + $this->EventCheckToDelete(['error_messages' => &$this->m_aDeleteIssues]); if ($this->InSyncScope()) { @@ -2810,16 +2812,12 @@ abstract class DBObject implements iDisplay } $sClass = get_class($this); - if ($sClass == 'UserRequest') { - IssueLog::Debug("CRUD: DBInsert $sClass::0 Requested", LogChannels::DM_CRUD); - } - $sRootClass = MetaModel::GetRootClass($sClass); // Ensure the update of the values (we are accessing the data directly) $this->DoComputeValues(); $this->OnInsert(); - $this->FireEvent(EVENT_SERVICE_DB_INSERT_REQUESTED); + $this->EventInsertRequested(); if ($this->m_iKey < 0) { @@ -2864,10 +2862,7 @@ abstract class DBObject implements iDisplay } } - if ($sClass == 'UserRequest') { - IssueLog::Debug("CRUD: DBInsert $sClass::0 About to write in DB", LogChannels::DM_CRUD); - } - $this->FireEvent(EVENT_SERVICE_DB_BEFORE_INSERT); + $this->EventInsertBefore(); $iTransactionRetry = 1; $bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled'); @@ -2953,15 +2948,11 @@ abstract class DBObject implements iDisplay $this->AfterInsert(); - if ($sClass == 'UserRequest') { - IssueLog::Debug("CRUD: $sClass::{$this->m_iKey} Inserted in DB", LogChannels::DM_CRUD); - } - // Prevent DBUpdate at this point (reentrance protection) $sClass = get_class($this); $sClassKey = $sClass.'::'.$this->m_iKey; self::$aUpdateReentrance[$sClassKey] = true; - $this->FireEvent(EVENT_SERVICE_DB_AFTER_INSERT_NO_RELOAD); + $this->EventInsertAfter(); unset(self::$aUpdateReentrance[$sClassKey]); // Activate any existing trigger @@ -3171,7 +3162,6 @@ abstract class DBObject implements iDisplay $this->m_iKey = self::GetNextTempId(get_class($this)); } - private static $aUpdateReentrance = []; /** * Update an object in DB @@ -3194,9 +3184,6 @@ abstract class DBObject implements iDisplay // Protect against reentrance (e.g. cascading the update of ticket logs) $sClass = get_class($this); $sKey = $sClass.'::'.$this->GetKey(); - if ($sClass == 'UserRequest') { - IssueLog::Debug("CRUD: DBUpdate $sKey Requested", LogChannels::DM_CRUD); - } if (array_key_exists($sKey, self::$aUpdateReentrance)) { @@ -3231,7 +3218,7 @@ abstract class DBObject implements iDisplay } } $this->OnUpdate(); - $this->FireEvent(EVENT_SERVICE_DB_UPDATE_REQUESTED); + $this->EventUpdateRequested(); // Freeze the changes at this point $this->InitPreviousValuesForUpdatedAttributes(); @@ -3295,10 +3282,7 @@ abstract class DBObject implements iDisplay $iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms'); $iTransactionRetry = $iTransactionRetryCount; } - if ($sClass == 'UserRequest') { - IssueLog::Debug("CRUD: DBUpdate $sKey About to be written in DB", LogChannels::DM_CRUD); - } - $this->FireEvent(EVENT_SERVICE_DB_BEFORE_UPDATE); + $this->EventUpdateBefore(); while ($iTransactionRetry > 0) { try @@ -3451,15 +3435,11 @@ abstract class DBObject implements iDisplay $this->AfterUpdate(); - if ($sClass == 'UserRequest') { - IssueLog::Debug("CRUD: DBUpdate $sKey Updated", LogChannels::DM_CRUD); - } - $this->FireEvent(EVENT_SERVICE_DB_AFTER_UPDATE_NO_RELOAD, ['changes' => $aChanges]); // Reload to get the external/computed attributes $this->Reload(true); - $this->FireEvent(EVENT_SERVICE_DB_AFTER_UPDATE, ['changes' => $aChanges]); + $this->EventUpdateAfter(['changes' => $aChanges]); } catch (Exception $e) { @@ -3681,7 +3661,7 @@ abstract class DBObject implements iDisplay } $this->OnDelete(); - $this->FireEvent(EVENT_SERVICE_DB_BEFORE_DELETE); + $this->EventDeleteBefore(); // Activate any existing trigger $sClass = get_class($this); @@ -3792,7 +3772,7 @@ abstract class DBObject implements iDisplay } $this->AfterDelete(); - $this->FireEvent(EVENT_SERVICE_DB_AFTER_DELETE); + $this->EventDeleteAfter(); $this->m_bIsInDB = false; @@ -5859,5 +5839,45 @@ abstract class DBObject implements iDisplay } EventService::FireEvent(new EventData($sEvent, $aEventSources, $aEventData)); } + + protected function EventInsertRequested() + { + } + + protected function EventInsertBefore() + { + } + + protected function EventInsertAfter() + { + } + + protected function EventCheckToWrite(array $aEventData) + { + } + + protected function EventCheckToDelete(array $aEventData) + { + } + + protected function EventUpdateRequested() + { + } + + protected function EventUpdateBefore() + { + } + + protected function EventUpdateAfter(array $aEventData) + { + } + + protected function EventDeleteBefore() + { + } + + protected function EventDeleteAfter() + { + } } diff --git a/datamodels/2.x/itop-attachments/datamodel.itop-attachments.xml b/datamodels/2.x/itop-attachments/datamodel.itop-attachments.xml index afaf41963..676a912a3 100755 --- a/datamodels/2.x/itop-attachments/datamodel.itop-attachments.xml +++ b/datamodels/2.x/itop-attachments/datamodel.itop-attachments.xml @@ -203,6 +203,40 @@ } }]]> + + /** + * @param array $aEventData + * + * @return void + * @throws \CoreException + */ + + false + protected + Overload-ExNihilo + FireEvent(EVENT_SERVICE_ATTACHMENT_AFTER_UPDATE); + } +]]> + + + /** + * @return void + * @throws \CoreException + */ + + false + protected + Overload-ExNihilo + FireEvent(EVENT_SERVICE_ATTACHMENT_AFTER_DELETE); + } +]]> +
@@ -253,4 +287,38 @@ + + + An attachment has been updated in database. + Attachment::AfterUpdate + + + The object updated + DBObject + + + Array of all the attributes changed + array + + + Debug string + string + + + + + An attachment has been deleted from database. + Attachment::AfterDelete + + + The object deleted + DBObject + + + Debug string + string + + + + diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index fdd8a9a3d..517947f72 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -1076,7 +1076,7 @@ EOF $oDescription = $oEvent->GetOptionalElement('description'); $sDescription = empty($oDescription) ? '' : $oDescription->GetText(''); - $sConstant = 'EVENT_SERVICE_'.strtoupper(SetupUtils::FromCamelCase($sName)); + $sConstant = $sName; $sOutput = "define('$sConstant', '$sName');\n"; $sOutput .= "Combodo\iTop\Service\EventService::RegisterEvent('$sName', '$sDescription', '$sModuleName');\n"; diff --git a/setup/setuputils.class.inc.php b/setup/setuputils.class.inc.php index f595f0101..d0b1e2c56 100644 --- a/setup/setuputils.class.inc.php +++ b/setup/setuputils.class.inc.php @@ -2174,20 +2174,6 @@ JS return $aOptionalExtensions; } - - - public static function FromCamelCase($sInput) { - $sPattern = '!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!'; - preg_match_all($sPattern, $sInput, $aMatches); - $aRet = $aMatches[0]; - foreach ($aRet as &$sMatch) { - $sMatch = $sMatch == strtoupper($sMatch) ? - strtolower($sMatch) : - lcfirst($sMatch); - } - return implode('_', $aRet); - } - } /** diff --git a/test/ItopDataTestCase.php b/test/ItopDataTestCase.php index 460c27139..ee4e44f3d 100644 --- a/test/ItopDataTestCase.php +++ b/test/ItopDataTestCase.php @@ -27,8 +27,10 @@ namespace Combodo\iTop\Test\UnitTest; */ use ArchivedObjectException; -use CMDBSource; use CMDBObject; +use CMDBSource; +use Combodo\iTop\Service\EventData; +use Combodo\iTop\Service\EventService; use Contact; use DBObject; use DBObjectSet; @@ -73,6 +75,9 @@ class ItopDataTestCase extends ItopTestCase // For cleanup private $aCreatedObjects = array(); + // Counts + public $aReloadCount = []; + const USE_TRANSACTION = true; const CREATE_TEST_ORG = false; @@ -96,6 +101,8 @@ class ItopDataTestCase extends ItopTestCase { $this->CreateTestOrganization(); } + + EventService::RegisterListener(EVENT_SERVICE_DB_OBJECT_RELOAD, [$this, 'CountObjectReload']); } /** @@ -790,6 +797,44 @@ class ItopDataTestCase extends ItopTestCase $this->iTestOrgId = $oOrg->GetKey(); } + public function ResetReloadCount() + { + $this->aReloadCount = []; + } + + public function DebugReloadCount($sMsg, $bResetCount = true) + { + $iTotalCount = 0; + $aTotalPerClass = []; + foreach ($this->aReloadCount as $sClass => $aCountByKeys) { + $iClassCount = 0; + foreach ($aCountByKeys as $iCount) { + $iClassCount += $iCount; + } + $iTotalCount += $iClassCount; + $aTotalPerClass[$sClass] = $iClassCount; + } + $this->debug("$sMsg - $iTotalCount reload(s)"); + foreach ($this->aReloadCount as $sClass => $aCountByKeys) { + $this->debug(" $sClass => $aTotalPerClass[$sClass] reload(s)"); + foreach ($aCountByKeys as $sKey => $iCount) { + $this->debug(" $sClass::$sKey => $iCount"); + } + } + if ($bResetCount) { + $this->ResetReloadCount(); + } + } + + public function CountObjectReload(EventData $oData) + { + $oObject = $oData->Get('object'); + $sClass = get_class($oObject); + $sKey = $oObject->GetKey(); + $iCount = $this->aReloadCount[$sClass][$sKey] ?? 0; + $this->aReloadCount[$sClass][$sKey] = 1 + $iCount; + } + /** * Assert that a series of operations will trigger a given number of MySL queries * @@ -811,7 +856,7 @@ class ItopDataTestCase extends ItopTestCase } else { - // Otherwise PHP Unit will consider that no assertion has been made + // Otherwise, PHP Unit will consider that no assertion has been made static::assertTrue(true); } } diff --git a/test/core/DBObjectTest.php b/test/core/DBObjectTest.php index 0a861ee63..7c0341b62 100644 --- a/test/core/DBObjectTest.php +++ b/test/core/DBObjectTest.php @@ -243,6 +243,51 @@ class DBObjectTest extends ItopDataTestCase }); } + /** + * @covers DBObject::NewObject + * @covers DBObject::Get + * @covers DBObject::Set + */ + public function testInsertNoReloadAttributeRefresh_ExternalKeysAndFields() + { + $this->ResetReloadCount(); + + static::assertDBQueryCount(0, function() use (&$oObject){ + $oObject = \MetaModel::NewObject('Person', array('name' => 'Foo', 'first_name' => 'John', 'org_id' => 3, 'location_id' => 2)); + }); + static::assertDBQueryCount(49, function() use (&$oObject) { + $oObject->DBInsertNoReload(); + }); + $this->DebugReloadCount("Person::DBInsertNoReload()"); + + static::assertDBQueryCount(3, function() use (&$oObject){ + static::assertEquals('Demo', $oObject->Get('org_id_friendlyname')); + static::assertEquals('Grenoble', $oObject->Get('location_id_friendlyname')); + }); + $this->DebugReloadCount("Get('org_id_friendlyname') and Get('location_id_friendlyname')"); + + // External key given as an id + static::assertDBQueryCount(1, function() use (&$oObject){ + $oObject->Set('org_id', 2); + static::assertEquals('IT Department', $oObject->Get('org_id_friendlyname')); + }); + $this->DebugReloadCount("Set('org_id', 2) andGet('org_id_friendlyname')"); + + // External key given as an object + static::assertDBQueryCount(1, function() use (&$oBordeaux){ + $oBordeaux = \MetaModel::GetObject('Location', 1); + }); + $this->DebugReloadCount("GetObject('Location', 1)"); + + static::assertDBQueryCount(5, function() use (&$oBordeaux, &$oObject){ + $oObject->Set('location_id', $oBordeaux); + static::assertEquals('IT Department', $oObject->Get('org_id_friendlyname')); + static::assertEquals('IT Department', $oObject->Get('org_name')); + static::assertEquals('Bordeaux', $oObject->Get('location_id_friendlyname')); + }); + $this->DebugReloadCount("Set('location_id',...) Get('org_id_friendlyname') Get('org_name') Get('location_id_friendlyname')"); + } + public function testSetExtKeyUnsetDependentAttribute() { $oObject = \MetaModel::NewObject('Person', array('name' => 'Foo', 'first_name' => 'John', 'org_id' => 3, 'location_id' => 2));