icingaweb2-module-director/library/Director/Db/Branch/BranchMerger.php

139 lines
4.3 KiB
PHP
Raw Normal View History

2021-08-16 11:43:09 +02:00
<?php
namespace Icinga\Module\Director\Db\Branch;
use Icinga\Module\Director\Data\Db\DbObject;
2021-10-05 22:27:32 +02:00
use Icinga\Module\Director\Data\Db\DbObjectTypeRegistry;
2021-08-16 11:43:09 +02:00
use Icinga\Module\Director\Db;
use Ramsey\Uuid\UuidInterface;
class BranchMerger
{
/** @var Branch */
protected $branchUuid;
/** @var Db */
protected $connection;
/** @var \Zend_Db_Adapter_Abstract */
protected $db;
/** @var array */
2021-10-05 22:27:32 +02:00
protected $ignoreActivities = [];
2021-08-16 11:43:09 +02:00
/** @var bool */
protected $ignoreDeleteWhenMissing = false;
/** @var bool */
protected $ignoreModificationWhenMissing = false;
/**
* Apply branch modifications
*
* TODO: allow to skip or ignore modifications, in case modified properties have
* been changed in the meantime
*
* @param UuidInterface $branchUuid
* @param Db $connection
*/
public function __construct(UuidInterface $branchUuid, Db $connection)
{
$this->branchUuid = $branchUuid;
$this->db = $connection->getDbAdapter();
$this->connection = $connection;
}
/**
* Skip a delete operation, when the object to be deleted does not exist
*
* @param bool $ignore
*/
public function ignoreDeleteWhenMissing($ignore = true)
{
$this->ignoreDeleteWhenMissing = $ignore;
}
/**
* Skip a modification, when the related object does not exist
* @param bool $ignore
*/
public function ignoreModificationWhenMissing($ignore = true)
{
$this->ignoreModificationWhenMissing = $ignore;
}
/**
2021-10-05 22:27:32 +02:00
* @param int $key
2021-08-16 11:43:09 +02:00
*/
2021-10-05 22:27:32 +02:00
public function ignoreActivity($key)
2021-08-16 11:43:09 +02:00
{
2021-10-05 22:27:32 +02:00
$this->ignoreActivities[$key] = true;
2021-08-16 11:43:09 +02:00
}
/**
2021-10-05 22:27:32 +02:00
* @param BranchActivity $activity
* @return bool
2021-08-16 11:43:09 +02:00
*/
2021-10-05 22:27:32 +02:00
public function ignoresActivity(BranchActivity $activity)
2021-08-16 11:43:09 +02:00
{
2021-10-05 22:27:32 +02:00
return isset($this->ignoreActivities[$activity->getTimestampNs()]);
2021-08-16 11:43:09 +02:00
}
/**
* @throws MergeError
*/
public function merge()
{
$this->connection->runFailSafeTransaction(function () {
2021-10-05 22:27:32 +02:00
$query = $this->db->select()
->from(BranchActivity::DB_TABLE)
->where('branch_uuid = ?', $this->connection->quoteBinary($this->branchUuid->getBytes()))
->order('timestamp_ns ASC');
$rows = $this->db->fetchAll($query);
2021-08-16 11:43:09 +02:00
foreach ($rows as $row) {
2021-10-05 22:27:32 +02:00
$activity = BranchActivity::fromDbRow($row);
$this->applyModification($activity);
2021-08-16 11:43:09 +02:00
}
2021-10-05 22:27:32 +02:00
(new BranchStore($this->connection))->deleteByUuid($this->branchUuid);
2021-08-16 11:43:09 +02:00
});
}
/**
2021-10-05 22:27:32 +02:00
* @param BranchActivity $activity
2021-08-16 11:43:09 +02:00
* @throws MergeError
* @throws \Icinga\Exception\NotFoundError
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
*/
2021-10-05 22:27:32 +02:00
protected function applyModification(BranchActivity $activity)
2021-08-16 11:43:09 +02:00
{
/** @var string|DbObject $class */
2021-10-05 22:27:32 +02:00
$class = DbObjectTypeRegistry::classByType($activity->getObjectTable());
$uuid = $activity->getObjectUuid();
2021-08-16 11:43:09 +02:00
2021-10-05 22:27:32 +02:00
$exists = $class::uniqueIdExists($uuid, $this->connection);
if ($activity->isActionCreate()) {
2021-08-16 11:43:09 +02:00
if ($exists) {
2021-10-05 22:27:32 +02:00
if (! $this->ignoresActivity($activity)) {
throw new MergeErrorRecreateOnMerge($activity);
2021-08-16 11:43:09 +02:00
}
} else {
2021-10-05 22:27:32 +02:00
$activity->createDbObject()->store($this->connection);
2021-08-16 11:43:09 +02:00
}
2021-10-05 22:27:32 +02:00
} elseif ($activity->isActionDelete()) {
2021-08-16 11:43:09 +02:00
if ($exists) {
2021-10-05 22:27:32 +02:00
$activity->deleteDbObject($this->connection);
} elseif (! $this->ignoreDeleteWhenMissing && ! $this->ignoresActivity($activity)) {
throw new MergeErrorDeleteMissingObject($activity);
2021-08-16 11:43:09 +02:00
}
} else {
if ($exists) {
2021-10-05 22:27:32 +02:00
$activity->applyToDbObject($class::requireWithUniqueId($uuid, $this->connection))->store();
// TODO: you modified an object, and related properties have been changed in the meantime.
// We're able to detect this with the given data, and might want to offer a rebase.
} elseif (! $this->ignoreModificationWhenMissing && ! $this->ignoresActivity($activity)) {
throw new MergeErrorModificationForMissingObject($activity);
2021-08-16 11:43:09 +02:00
}
}
}
}