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; } /** * @param int $key */ public function ignoreActivity($key) { $this->ignoreActivities[$key] = true; } /** * @param BranchActivity $activity * @return bool */ public function ignoresActivity(BranchActivity $activity) { return isset($this->ignoreActivities[$activity->getTimestampNs()]); } /** * @throws MergeError */ public function merge($comment = null) { $username = DirectorActivityLog::username(); $this->connection->runFailSafeTransaction(function () use ($comment, $username) { $formerActivityId = (int) DirectorActivityLog::loadLatest($this->connection)->get('id'); $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); foreach ($rows as $row) { $activity = BranchActivity::fromDbRow($row); $author = $activity->getAuthor(); if ($username !== $author) { DirectorActivityLog::overrideUsername("$author/$username"); } $this->applyModification($activity); } (new BranchStore($this->connection))->deleteByUuid($this->branchUuid); $currentActivityId = (int) DirectorActivityLog::loadLatest($this->connection)->get('id'); $firstActivityId = (int) $this->db->fetchOne( $this->db->select()->from('director_activity_log', 'MIN(id)')->where('id > ?', $formerActivityId) ); if ($comment && strlen($comment)) { $this->db->insert('director_activity_log_remark', [ 'first_related_activity' => $firstActivityId, 'last_related_activity' => $currentActivityId, 'remark' => $comment, ]); } }); DirectorActivityLog::restoreUsername(); } /** * @param BranchActivity $activity * @throws MergeError * @throws \Icinga\Exception\NotFoundError * @throws \Icinga\Module\Director\Exception\DuplicateKeyException */ protected function applyModification(BranchActivity $activity) { /** @var string|DbObject $class */ $class = DbObjectTypeRegistry::classByType($activity->getObjectTable()); $uuid = $activity->getObjectUuid(); $exists = $class::uniqueIdExists($uuid, $this->connection); if ($activity->isActionCreate()) { if ($exists) { if (! $this->ignoresActivity($activity)) { throw new MergeErrorRecreateOnMerge($activity); } } else { $activity->createDbObject($this->connection)->store($this->connection); } } elseif ($activity->isActionDelete()) { if ($exists) { $activity->deleteDbObject($class::requireWithUniqueId($uuid, $this->connection)); } elseif (! $this->ignoreDeleteWhenMissing && ! $this->ignoresActivity($activity)) { throw new MergeErrorDeleteMissingObject($activity); } } else { if ($exists) { $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); } } } }