/** * @depends testNewSearch */ public function testModifySearch($newSearchData) { $key = $newSearchData['key']; $version = $newSearchData['version']; $json = json_decode($newSearchData['content'], true); // Remove one search condition array_shift($json['conditions']); $name = $json['name']; $conditions = $json['conditions']; $response = API::userPut(self::$config['userID'], "searches/{$key}?key=" . self::$config['apiKey'], json_encode($json), array("Content-Type: application/json", "If-Unmodified-Since-Version: {$version}")); $this->assert204($response); $xml = API::getSearchXML($key); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content']); $this->assertEquals($name, (string) $json->name); $this->assertInternalType('array', $json->conditions); $this->assertCount(sizeOf($conditions), $json->conditions); foreach ($conditions as $i => $condition) { foreach ($condition as $key => $val) { $this->assertEquals($val, $json->conditions[$i]->{$key}); } } }
public function testCreatorSummary() { $xml = API::createItem("book", array("creators" => array(array("creatorType" => "author", "name" => "Test"))), $this); $data = API::parseDataFromAtomEntry($xml); $itemKey = $data['key']; $json = json_decode($data['content'], true); $creatorSummary = (string) array_shift($xml->xpath('//atom:entry/zapi:creatorSummary')); $this->assertEquals("Test", $creatorSummary); $json['creators'][] = array("creatorType" => "author", "firstName" => "Alice", "lastName" => "Foo"); $response = API::userPut(self::$config['userID'], "items/{$itemKey}?key=" . self::$config['apiKey'], json_encode($json)); $this->assert204($response); $xml = API::getItemXML($itemKey); $creatorSummary = (string) array_shift($xml->xpath('//atom:entry/zapi:creatorSummary')); $this->assertEquals("Test and Foo", $creatorSummary); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content'], true); $json['creators'][] = array("creatorType" => "author", "firstName" => "Bob", "lastName" => "Bar"); $response = API::userPut(self::$config['userID'], "items/{$itemKey}?key=" . self::$config['apiKey'], json_encode($json)); $this->assert204($response); $xml = API::getItemXML($itemKey); $creatorSummary = (string) array_shift($xml->xpath('//atom:entry/zapi:creatorSummary')); $this->assertEquals("Test et al.", $creatorSummary); }
public function testDeleteCollectionRelation() { $relations = array("owl:sameAs" => "http://zotero.org/groups/1/collections/AAAAAAAA"); $data = API::createCollection("Test", array("relations" => $relations), $this, 'data'); $json = json_decode($data['content'], true); // Remove all relations $json['relations'] = new \stdClass(); unset($relations['owl:sameAs']); $response = API::userPut(self::$config['userID'], "collections/{$data['key']}?key=" . self::$config['apiKey'], json_encode($json)); $this->assert204($response); // Make sure it's gone $xml = API::getCollectionXML($data['key']); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content'], true); $this->assertCount(sizeOf($relations), $json['relations']); foreach ($relations as $predicate => $object) { $this->assertEquals($object, $json['relations'][$predicate]); } }
private function _testDeleteAndDeleted($objectType) { API::userClear(self::$config['userID']); $objectTypePlural = API::getPluralObjectType($objectType); $xml = Sync::updated(self::$sessionID); $lastSyncTimestamp = (int) $xml['timestamp']; // Create via sync switch ($objectType) { case 'item': $keys[] = Sync::createItem(self::$sessionID, self::$config['libraryID'], "book", false, $this); break; case 'setting': $settingKey = "tagColors"; $response = API::userPut(self::$config['userID'], "settings/{$settingKey}?key=" . self::$config['apiKey'], json_encode(array("value" => array(array("name" => "_READ", "color" => "#990000")))), array("Content-Type: application/json", "If-Unmodified-Since-Version: 0")); $this->assertEquals(204, $response->getStatus()); $keys[] = $settingKey; break; } // Check via API foreach ($keys as $key) { $response = API::userGet(self::$config['userID'], "{$objectTypePlural}/{$key}?key=" . self::$config['apiKey']); $this->assertEquals(200, $response->getStatus()); $version = $response->getHeader("Last-Modified-Version"); $this->assertNotNull($version); } // Get empty deleted via API $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newer={$version}"); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertEmpty($json[$objectTypePlural]); // Get empty deleted via API with newertime $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newertime={$lastSyncTimestamp}"); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertEmpty($json[$objectTypePlural]); // Delete via sync foreach ($keys as $key) { switch ($objectType) { case 'item': Sync::deleteItem(self::$sessionID, self::$config['libraryID'], $key, $this); break; case 'setting': // Delete via sync $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; $xmlstr = '<data version="9">' . '<deleted>' . '<settings>' . '<setting libraryID="' . self::$config['libraryID'] . '" key="' . $key . '"/>' . '</settings>' . '</deleted>' . '</data>'; $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr); Sync::waitForUpload(self::$sessionID, $response, $this); break; } } // Check 404 via API foreach ($keys as $key) { $response = API::userGet(self::$config['userID'], "{$objectTypePlural}/{$key}?key=" . self::$config['apiKey']); $this->assertEquals(404, $response->getStatus()); } // Get deleted via API $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newer={$version}"); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertArrayHasKey($objectTypePlural, $json); $this->assertCount(sizeOf($keys), $json[$objectTypePlural]); foreach ($keys as $key) { $this->assertContains($key, $json[$objectTypePlural]); } // Get deleted via API with newertime $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newertime={$lastSyncTimestamp}"); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertArrayHasKey($objectTypePlural, $json); $this->assertCount(sizeOf($keys), $json[$objectTypePlural]); foreach ($keys as $key) { $this->assertContains($key, $json[$objectTypePlural]); } // Should be empty with later newertime $xml = Sync::updated(self::$sessionID); $lastSyncTimestamp = (int) $xml['timestamp']; $response = API::userGet(self::$config['userID'], "deleted?key=" . self::$config['apiKey'] . "&newertime=" . ($lastSyncTimestamp + 2)); $this->assertEquals(200, $response->getStatus()); $json = json_decode($response->getBody(), true); $this->assertEmpty($json[$objectTypePlural]); }
public function testAddFileClientZip() { API::userClear(self::$config['userID']); $auth = array('username' => self::$config['username'], 'password' => self::$config['password']); // Get last storage sync $response = API::userGet(self::$config['userID'], "laststoragesync?auth=1", array(), $auth); $this->assert404($response); $xml = API::createItem("book", false, $this); $data = API::parseDataFromAtomEntry($xml); $key = $data['key']; $fileContentType = "text/html"; $fileCharset = "UTF-8"; $fileFilename = "file.html"; $fileModtime = time(); $xml = API::createAttachmentItem("imported_url", [], $key, $this); $data = API::parseDataFromAtomEntry($xml); $key = $data['key']; $version = $data['version']; $json = json_decode($data['content']); $json->contentType = $fileContentType; $json->charset = $fileCharset; $json->filename = $fileFilename; $response = API::userPut(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'], json_encode($json), array("Content-Type: application/json")); $this->assert204($response); // Get a sync timestamp from before the file is updated sleep(1); require_once 'include/sync.inc.php'; $sessionID = Sync::login(); $xml = Sync::updated($sessionID); $lastsync = (int) $xml['timestamp']; Sync::logout($sessionID); // Get file info $response = API::userGet(self::$config['userID'], "items/{$data['key']}/file?auth=1&iskey=1&version=1&info=1", array(), $auth); $this->assert404($response); $zip = new ZipArchive(); $file = "work/{$key}.zip"; if ($zip->open($file, ZIPARCHIVE::CREATE) !== TRUE) { throw new Exception("Cannot open ZIP file"); } $zip->addFromString($fileFilename, self::getRandomUnicodeString()); $zip->addFromString("file.css", self::getRandomUnicodeString()); $zip->close(); $hash = md5_file($file); $filename = $key . ".zip"; $size = filesize($file); $fileContents = file_get_contents($file); // Get upload authorization $response = API::userPost(self::$config['userID'], "items/{$data['key']}/file?auth=1&iskey=1&version=1", $this->implodeParams(array("md5" => $hash, "filename" => $filename, "filesize" => $size, "mtime" => $fileModtime, "zip" => 1)), array("Content-Type: application/x-www-form-urlencoded"), $auth); $this->assert200($response); $this->assertContentType("application/xml", $response); $xml = new SimpleXMLElement($response->getBody()); self::$toDelete[] = "{$hash}"; $boundary = "---------------------------" . rand(); $postData = ""; foreach ($xml->params->children() as $key => $val) { $postData .= "--" . $boundary . "\r\nContent-Disposition: form-data; " . "name=\"{$key}\"\r\n\r\n{$val}\r\n"; } $postData .= "--" . $boundary . "\r\nContent-Disposition: form-data; " . "name=\"file\"\r\n\r\n" . $fileContents . "\r\n"; $postData .= "--" . $boundary . "--"; // Upload to S3 $response = HTTP::post((string) $xml->url, $postData, array("Content-Type: multipart/form-data; boundary=" . $boundary)); $this->assert201($response); // // Register upload // $response = API::userPost(self::$config['userID'], "items/{$data['key']}/file?auth=1&iskey=1&version=1", "update=" . $xml->key . "&mtime=" . $fileModtime, array("Content-Type: application/x-www-form-urlencoded"), $auth); $this->assert204($response); // Verify attachment item metadata $response = API::userGet(self::$config['userID'], "items/{$data['key']}?key=" . self::$config['apiKey'] . "&content=json"); $xml = API::getXMLFromResponse($response); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content']); $this->assertEquals($hash, $json->md5); $this->assertEquals($fileFilename, $json->filename); $this->assertEquals($fileModtime, $json->mtime); // Make sure attachment item wasn't updated (or else the client // will get a conflict when it tries to update the metadata) $sessionID = Sync::login(); $xml = Sync::updated($sessionID, $lastsync); Sync::logout($sessionID); $this->assertEquals(0, $xml->updated[0]->count()); $response = API::userGet(self::$config['userID'], "laststoragesync?auth=1", array(), array('username' => self::$config['username'], 'password' => self::$config['password'])); $this->assert200($response); $mtime = $response->getBody(); $this->assertRegExp('/^[0-9]{10}$/', $mtime); // File exists $response = API::userPost(self::$config['userID'], "items/{$data['key']}/file?auth=1&iskey=1&version=1", $this->implodeParams(array("md5" => $hash, "filename" => $filename, "filesize" => $size, "mtime" => $fileModtime + 1000, "zip" => 1)), array("Content-Type: application/x-www-form-urlencoded"), $auth); $this->assert200($response); $this->assertContentType("application/xml", $response); $this->assertEquals("<exists/>", $response->getBody()); // Make sure attachment item still wasn't updated $sessionID = Sync::login(); $xml = Sync::updated($sessionID, $lastsync); Sync::logout($sessionID); $this->assertEquals(0, $xml->updated[0]->count()); }
public function testParentItem() { $xml = API::createItem("book", false, $this); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content'], true); $parentKey = $data['key']; $parentVersion = $data['version']; $xml = API::createAttachmentItem("linked_url", [], $parentKey, $this); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content'], true); $childKey = $data['key']; $childVersion = $data['version']; $this->assertArrayHasKey('parentItem', $json); $this->assertEquals($parentKey, $json['parentItem']); // Remove the parent, making the child a standalone attachment unset($json['parentItem']); // Remove version property, to test header unset($json['itemVersion']); // The parent item version should have been updated when a child // was added, so this should fail $response = API::userPut(self::$config['userID'], "items/{$childKey}?key=" . self::$config['apiKey'], json_encode($json), array("If-Unmodified-Since-Version: " . $parentVersion)); $this->assert412($response); $response = API::userPut(self::$config['userID'], "items/{$childKey}?key=" . self::$config['apiKey'], json_encode($json), array("If-Unmodified-Since-Version: " . $childVersion)); $this->assert204($response); $xml = API::getItemXML($childKey); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content'], true); $this->assertArrayNotHasKey('parentItem', $json); }
private function _testUploadUnmodified($objectType) { $objectTypePlural = API::getPluralObjectType($objectType); switch ($objectType) { case 'collection': $xml = API::createCollection("Name", false, $this); break; case 'item': $xml = API::createItem("book", array("title" => "Title"), $this); break; case 'search': $xml = API::createSearch("Name", 'default', $this); break; } $version = (int) array_shift($xml->xpath('//atom:entry/zapi:version')); $this->assertNotEquals(0, $version); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content']); $response = API::userPut(self::$config['userID'], "{$objectTypePlural}/{$data['key']}?key=" . self::$config['apiKey'], json_encode($json)); $this->assert204($response); $this->assertEquals($version, $response->getHeader("Last-Modified-Version")); switch ($objectType) { case 'collection': $xml = API::getCollectionXML($data['key']); break; case 'item': $xml = API::getItemXML($data['key']); break; case 'search': $xml = API::getSearchXML($data['key']); break; } $data = API::parseDataFromAtomEntry($xml); $this->assertEquals($version, $data['version']); }
public function testCollectionChildItemError() { $collectionKey = API::createCollection('Test', false, $this, 'key'); $key = API::createItem("book", array(), $this, 'key'); $xml = API::createNoteItem("Test Note", $key, $this, 'atom'); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content'], true); $json['collections'] = array($collectionKey); $json['relations'] = new stdClass(); $libraryVersion = API::getLibraryVersion(); $response = API::userPut(self::$config['userID'], "items/{$data['key']}?key=" . self::$config['apiKey'], json_encode($json), ["Content-Type: application/json"]); $this->assert400($response); $this->assertEquals("Child items cannot be assigned to collections", $response->getBody()); }
public function testDeleteItemContent() { $key = API::createItem("book", false, $this, 'key'); $xml = API::createAttachmentItem("imported_file", [], $key, $this, 'atom'); $data = API::parseDataFromAtomEntry($xml); $content = "Ыюм мютат дэбетиз конвынёры эю, ку мэль жкрипта трактатоз.\nПро ут чтэт эрепюят граэкйж, дуо нэ выро рыкючабо пырикюлёз."; // Store content $response = API::userPut(self::$config['userID'], "items/{$data['key']}/fulltext?key=" . self::$config['apiKey'], json_encode(["content" => $content, "indexedPages" => 50]), array("Content-Type: application/json")); $this->assert204($response); $contentVersion = $response->getHeader("Last-Modified-Version"); // Retrieve it $response = API::userGet(self::$config['userID'], "items/{$data['key']}/fulltext?key=" . self::$config['apiKey']); $this->assert200($response); $json = json_decode($response->getBody(), true); $this->assertEquals($content, $json['content']); $this->assertEquals(50, $json['indexedPages']); // Set to empty string $response = API::userPut(self::$config['userID'], "items/{$data['key']}/fulltext?key=" . self::$config['apiKey'], json_encode(["content" => ""]), array("Content-Type: application/json")); $this->assert204($response); $this->assertGreaterThan($contentVersion, $response->getHeader("Last-Modified-Version")); // Make sure it's gone $response = API::userGet(self::$config['userID'], "items/{$data['key']}/fulltext?key=" . self::$config['apiKey']); $this->assert200($response); $json = json_decode($response->getBody(), true); $this->assertEquals("", $json['content']); $this->assertArrayNotHasKey("indexedPages", $json); }
public function testOverlongSetting() { $settingKey = "tagColors"; $value = array(array("name" => $this->content = str_repeat("abcdefghij", 1001), "color" => "#990000")); $json = array("value" => $value, "version" => 0); $response = API::userPut(self::$config['userID'], "settings/{$settingKey}?key=" . self::$config['apiKey'], json_encode($json), array("Content-Type: application/json")); $this->assert400($response, "'value' cannot be longer than 1000 characters"); }
public function testEditSingleCollection() { API::useAPIVersion(2); $xml = API::createCollection("Test", false, $this); $data = API::parseDataFromAtomEntry($xml); $key = $data['key']; $version = $data['version']; API::useAPIVersion(1); $xml = API::getCollectionXML($data['key']); $etag = (string) array_shift($xml->xpath('//atom:entry/atom:content/@etag')); $this->assertNotNull($etag); $newName = "Test 2"; $json = array("name" => $newName, "parent" => false); $response = API::userPut(self::$config['userID'], "collections/{$key}?key=" . self::$config['apiKey'], json_encode($json), array("Content-Type: application/json", "If-Match: {$etag}")); $this->assert200($response); $xml = API::getXMLFromResponse($response); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content']); $this->assertEquals($newName, (string) $json->name); }
public function testComputerProgram() { $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; $itemKey = 'AAAAAAAA'; // Create item via sync $data = '<data version="9"><items><item libraryID="' . self::$config['libraryID'] . '" itemType="computerProgram" ' . 'dateAdded="2009-03-07 04:53:20" ' . 'dateModified="2009-03-07 04:54:09" ' . 'key="' . $itemKey . '">' . '<field name="version">1.0</field>' . '</item></items></data>'; $response = Sync::upload(self::$sessionID, $updateKey, $data); Sync::waitForUpload(self::$sessionID, $response, $this); // Get item version via API $response = API::userGet(self::$config['userID'], "items/{$itemKey}?key=" . self::$config['apiKey'] . "&content=json"); $this->assertEquals(200, $response->getStatus()); $xml = API::getItemXML($itemKey); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content'], true); $this->assertEquals('1.0', $json['version']); $json['version'] = '1.1'; $response = API::userPut(self::$config['userID'], "items/{$itemKey}?key=" . self::$config['apiKey'], json_encode($json)); $this->assertEquals(204, $response->getStatus()); $xml = Sync::updated(self::$sessionID); $this->assertEquals('version', (string) $xml->updated[0]->items[0]->item[0]->field[0]['name']); }
public function testCreatorItemChangeViaAPI() { $key = 'AAAAAAAA'; $xml = Sync::updated(self::$sessionID); $updateKey = (string) $xml['updateKey']; // Create item via sync $data = '<data version="9"><items><item libraryID="' . self::$config['libraryID'] . '" itemType="book" ' . 'dateAdded="2009-03-07 04:53:20" ' . 'dateModified="2009-03-07 04:54:09" ' . 'key="' . $key . '">' . '<creator key="BBBBBBBB" creatorType="author" index="0">' . '<creator libraryID="' . self::$config['libraryID'] . '" ' . 'key="BBBBBBBB" dateAdded="2009-03-07 04:53:20" dateModified="2009-03-07 04:54:09">' . '<firstName>First</firstName>' . '<lastName>Last</lastName>' . '<fieldMode>0</fieldMode>' . '</creator></creator></item></items></data>'; $response = Sync::upload(self::$sessionID, $updateKey, $data); Sync::waitForUpload(self::$sessionID, $response, $this); // Get item version via API and check creatorSummary API::useAPIVersion(1); $response = API::userGet(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'] . "&content=json"); $xml = API::getXMLFromResponse($response); $creatorSummary = (string) array_shift($xml->xpath('//atom:entry/zapi:creatorSummary')); $this->assertEquals("Last", $creatorSummary); $data = API::parseDataFromAtomEntry($xml); $etag = (string) array_shift($xml->xpath('//atom:entry/atom:content/@zapi:etag')); $this->assertNotEquals("", $etag); // Modify creator $json = json_decode($data['content'], true); $json['creators'][0] = array("name" => "First Last", "creatorType" => "author"); // Modify via API $response = API::userPut(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'], json_encode($json), array("If-Match: {$etag}")); $xml = API::getXMLFromResponse($response); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content']); $creatorSummary = (string) array_shift($xml->xpath('//atom:entry/zapi:creatorSummary')); $this->assertEquals("First Last", $creatorSummary); $this->assertTrue(isset($json->creators[0]->name)); $this->assertEquals("First Last", $json->creators[0]->name); $newETag = (string) array_shift($xml->xpath('//atom:entry/zapi:etag')); $this->assertNotEquals($etag, $newETag); // Get item again via API $response = API::userGet(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'] . "&content=json"); $xml = API::getXMLFromResponse($response); $data = API::parseDataFromAtomEntry($xml); $json = json_decode($data['content']); $creatorSummary = (string) array_shift($xml->xpath('//atom:entry/zapi:creatorSummary')); $this->assertEquals("First Last", $creatorSummary); $this->assertTrue(isset($json->creators[0]->name)); $this->assertEquals("First Last", $json->creators[0]->name); $newETag = (string) array_shift($xml->xpath('//atom:entry/zapi:etag')); $this->assertNotEquals($etag, $newETag); }