/** * Saves a node to the Ldap store * * @param Node $node Node to be saved * * @return boolean True if node got created, false if it was updated * * @throws PersistenceException If saving operation fails writing to the Ldap */ public function save(Node $node) { $this->validateBinding(); if (strlen(trim($node->getDn())) == 0) { throw new PersistenceException('Cannot save: dn missing for the entry'); } if (!$node->isHydrated()) { try { $origin = $this->getNode($node->getDn()); $node->rebaseDiff($origin); } catch (NodeNotFoundException $e) { $this->connection->addEntry($node->getDn(), $node->getRawAttributes()); $node->snapshot(); return true; } } if (count($data = $node->getDiffAdditions()) > 0) { $this->connection->addAttributeValues($node->getDn(), $data); } if (count($data = $node->getDiffDeletions()) > 0) { $this->connection->deleteAttributeValues($node->getDn(), $data); } if (count($data = $node->getDiffReplacements()) > 0) { $this->connection->replaceAttributeValues($node->getDn(), $data); } $node->snapshot(); return false; }
/** * Tests rebasing a node changes on an existing node * * @return void */ public function testRebaseDiff() { $rebasedNode = new Node(); $rebasedNode->setDn('rebased'); $rebasedNode->get('a', true)->add(array('a2', 'a4')); $rebasedNode->get('b', true)->add(array('b1', 'b3')); $rebasedNode->get('c', true)->add(array('c1', 'c3')); $rebasedNode->get('d', true)->add(array('d1', 'd2', 'd3', 'd4')); $rebasedNode->get('g', true)->add('g1'); $rebasedNode->get('h', true)->add(array('h1', 'h2')); $rebasedNode->get('i', true)->add(array('i1', 'i2')); $rebasedNode->snapshot(); $rebasedNode->get('a')->add(array('a1', 'a3')); $rebasedNode->removeAttribute('b'); $rebasedNode->get('c')->set(array('c4', 'c5')); $rebasedNode->get('d')->remove('d2'); $rebasedNode->get('d')->remove('d3'); $rebasedNode->get('d')->add('d5'); $rebasedNode->get('f', true)->add(array('f1', 'f2')); $rebasedNode->removeAttribute('g'); $rebasedNode->get('h')->set(array('h1', 'h3')); $rebasedNode->get('i')->remove('i2'); $this->assertEquals(array('a' => array('a2', 'a4', 'a1', 'a3'), 'c' => array('c4', 'c5'), 'd' => array(0 => 'd1', 3 => 'd4', 4 => 'd5'), 'f' => array('f1', 'f2'), 'h' => array('h1', 'h3'), 'i' => array('i1')), $rebasedNode->getRawAttributes(), 'All attributes according to plan'); $this->assertEquals(array('a' => array('a1', 'a3'), 'd' => array('d5'), 'f' => array('f1', 'f2')), $rebasedNode->getDiffAdditions(), 'Regular additions tracking'); $this->assertEquals(array('b' => array(), 'd' => array('d2', 'd3'), 'g' => array(), 'i' => array('i2')), $rebasedNode->getDiffDeletions(), 'Regular deletions tracking'); $this->assertEquals(array('c' => array('c4', 'c5'), 'h' => array('h1', 'h3')), $rebasedNode->getDiffReplacements(), 'Regular replacements tracking'); $origNode = new Node(); $origNode->setDn('origin'); $origNode->get('a', true)->add(array('a1', 'a2')); $origNode->get('b', true)->add(array('b1', 'b2')); $origNode->get('c', true)->add(array('c1', 'c2')); $origNode->get('d', true)->add(array('d1', 'd2')); $origNode->get('e', true)->add(array('e1', 'e2')); try { $rebasedNode->rebaseDiff($origNode); $this->fail('Cannot rebase on a node which is not snapshot'); } catch (RebaseException $e) { $this->assertRegExp('/origin has some uncommitted changes - Cannot rebase rebased on origin/', $e->getMessage()); } $this->assertEquals(array('a' => array('a2', 'a4', 'a1', 'a3'), 'c' => array('c4', 'c5'), 'd' => array(0 => 'd1', 3 => 'd4', 4 => 'd5'), 'f' => array('f1', 'f2'), 'h' => array('h1', 'h3'), 'i' => array('i1')), $rebasedNode->getRawAttributes(), 'Rebased node values are unchanged'); $origNode->snapshot(); $backupNode = clone $origNode; $rebasedNode->rebaseDiff($origNode); $this->assertEquals(array('a' => array('a1', 'a2', 'a3'), 'c' => array('c4', 'c5'), 'd' => array(0 => 'd1', 2 => 'd5'), 'e' => array('e1', 'e2'), 'f' => array('f1', 'f2'), 'h' => array('h1', 'h3')), $rebasedNode->getRawAttributes(), 'Rebased diff got applied on origin node values'); $this->assertEquals(array('a' => array('a3'), 'd' => array('d5'), 'f' => array('f1', 'f2'), 'h' => array('h1', 'h3')), $rebasedNode->getDiffAdditions(), 'A new additions diff has been computed (h did not exist in origin node so it is added)'); $this->assertEquals(array('b' => array(), 'd' => array('d2')), $rebasedNode->getDiffDeletions(), 'g and i deletions got ignored in the new deletion diff as they were not set on origin'); $this->assertEquals(array('c' => array('c4', 'c5')), $rebasedNode->getDiffReplacements(), 'h replacement was computed as an addition on origin node'); }
/** * Tests complex updates with changeset merging when saving * * @return void */ public function testSaveMergesChanges() { $manager = new Manager($this->minimal, $this->driver); $manager->connect(); $manager->bind(); $entry = new Entry('test_dn', array('a' => array('a1', 'a2'), 'b' => array('b1', 'b2'), 'c' => array('c1', 'c2'), 'd' => array('d1', 'd2'), 'e' => array('e1', 'e2'))); $this->driver->getConnection()->stackResults(array($entry)); $node = new Node(); $node->setDn('test_dn'); $node->get('a', true)->add(array('a2', 'a4')); $node->get('b', true)->add(array('b1', 'b3')); $node->get('c', true)->add(array('c1', 'c3')); $node->get('d', true)->add(array('d1', 'd2', 'd3', 'd4')); $node->get('g', true)->add('g1'); $node->get('h', true)->add(array('h1', 'h2')); $node->get('i', true)->add(array('i1', 'i2')); $node->snapshot(false); $node->get('a')->add(array('a1', 'a3')); $node->removeAttribute('b'); $node->get('c')->set(array('c4', 'c5')); $node->get('d')->remove('d2'); $node->get('d')->remove('d3'); $node->get('d')->add('d5'); $node->get('f', true)->add(array('f1', 'f2')); $node->removeAttribute('g'); $node->get('h')->set(array('h1', 'h3')); $node->get('i')->remove('i2'); $this->assertFalse($manager->save($node), 'Node persistence resulted in an update'); $this->assertSearchLog($this->driver->getConnection()->shiftLog(), 'test_dn', '(objectclass=*)', SearchInterface::SCOPE_BASE, null, array($entry)); $this->assertActionLog($this->driver->getConnection()->shiftLog(), 'attr_add', 'test_dn', array('a' => array('a3'), 'd' => array('d5'), 'f' => array('f1', 'f2'), 'h' => array('h1', 'h3'))); $this->assertActionLog($this->driver->getConnection()->shiftLog(), 'attr_del', 'test_dn', array('b' => array(), 'd' => array('d2'))); $this->assertActionLog($this->driver->getConnection()->shiftLog(), 'attr_rep', 'test_dn', array('c' => array('c4', 'c5'))); $this->assertNull($this->driver->getConnection()->shiftLog(), 'All logs have been parsed'); $this->assertSnapshot($node, 'A node is snapshot after update'); }