public function testExportInclude() { foreach (self::$formats as $format) { $response = API::userGet(self::$config['userID'], "items?include={$format}"); $this->assert200($response); $json = API::getJSONFromResponse($response); foreach ($json as $obj) { $this->assertEquals(self::$items[$obj['key']][$format], $obj[$format]); } } }
public function testUserGroupsAnonymousJSON() { API::useAPIKey(false); $response = API::get("users/" . self::$config['userID'] . "/groups"); $this->assert200($response); $this->assertTotalResults(self::$config['numPublicGroups'], $response); // Make sure they're the right groups $json = API::getJSONFromResponse($response); $groupIDs = array_map(function ($data) { return $data['id']; }, $json); $this->assertContains(self::$config['ownedPublicGroupID'], $groupIDs); $this->assertContains(self::$config['ownedPublicNoAnonymousGroupID'], $groupIDs); }
public function testEditMultipleCollections() { $collection1Data = API::createCollection("Test 1", false, $this, 'jsonData'); $collection2Name = "Test 2"; $collection2Data = API::createCollection($collection2Name, false, $this, 'jsonData'); $collection1NewName = "Test 1 Modified"; $collection2NewParentKey = API::createCollection("Test 3", false, $this, 'key'); $response = API::userPost(self::$config['userID'], "collections", json_encode([['key' => $collection1Data['key'], 'version' => $collection1Data['version'], 'name' => $collection1NewName], ['key' => $collection2Data['key'], 'version' => $collection2Data['version'], 'parentCollection' => $collection2NewParentKey]]), ["Content-Type: application/json"]); $this->assert200($response); $json = API::getJSONFromResponse($response); $this->assertCount(2, $json['success']); $response = API::getCollectionResponse($json['success']); $this->assertTotalResults(2, $response); $json = API::getJSONFromResponse($response); // POST follows PATCH behavior, so unspecified values shouldn't change $this->assertEquals($collection1NewName, $json[0]['data']['name']); $this->assertFalse($json[0]['data']['parentCollection']); $this->assertEquals($collection2Name, $json[1]['data']['name']); $this->assertEquals($collection2NewParentKey, $json[1]['data']['parentCollection']); }
public function testKeyCreateAndModifyWithCredentials() { API::useAPIKey(""); $name = "Test " . uniqid(); // Can't create on /users/:userID/keys with credentials $response = API::userPost(self::$config['userID'], 'keys', json_encode(['username' => self::$config['username'], 'password' => self::$config['password'], 'name' => $name, 'access' => ['user' => ['library' => true]]])); $this->assert403($response); // Create with credentials $response = API::post('keys', json_encode(['username' => self::$config['username'], 'password' => self::$config['password'], 'name' => $name, 'access' => ['user' => ['library' => true]]]), [], []); $this->assert201($response); $json = API::getJSONFromResponse($response); $key = $json['key']; $this->assertEquals($json['userID'], self::$config['userID']); $this->assertEquals($json['name'], $name); $this->assertEquals(['user' => ['library' => true, 'files' => true]], $json['access']); $name = "Test " . uniqid(); // Can't modify on /users/:userID/keys/:key with credentials $response = API::userPut(self::$config['userID'], "keys/{$key}", json_encode(['username' => self::$config['username'], 'password' => self::$config['password'], 'name' => $name, 'access' => ['user' => ['library' => true]]])); $this->assert403($response); // Modify with credentials $response = API::put("keys/{$key}", json_encode(['username' => self::$config['username'], 'password' => self::$config['password'], 'name' => $name, 'access' => ['user' => ['library' => true]]])); $this->assert200($response); $json = API::getJSONFromResponse($response); $key = $json['key']; $this->assertEquals($json['name'], $name); $response = API::userDelete(self::$config['userID'], "keys/{$key}"); $this->assert204($response); }
public function testTrash() { API::userClear(self::$config['userID']); $key1 = API::createItem("book", false, $this, 'key'); $key2 = API::createItem("book", [ "deleted" => 1 ], $this, 'key'); // Item should show up in trash $response = API::userGet( self::$config['userID'], "items/trash" ); $json = API::getJSONFromResponse($response); $this->assertCount(1, $json); $this->assertEquals($key2, $json[0]['key']); // And not show up in main items $response = API::userGet( self::$config['userID'], "items" ); $json = API::getJSONFromResponse($response); $this->assertCount(1, $json); $this->assertEquals($key1, $json[0]['key']); // Including with ?itemKey $response = API::userGet( self::$config['userID'], "items?itemKey=" . $key2 ); $json = API::getJSONFromResponse($response); $this->assertCount(0, $json); }
<?php // // Check for existing groups, make sure they have the right permissions, // and delete any others // require_once __DIR__ . '/api3.inc.php'; $response = API3::superGet("users/" . $config['userID'] . "/groups"); $groups = API3::getJSONFromResponse($response); $config['ownedPublicGroupID'] = false; $config['ownedPublicNoAnonymousGroupID'] = false; $toDelete = []; foreach ($groups as $group) { $data = $group['data']; $id = $data['id']; $type = $data['type']; $owner = $data['owner']; $libraryReading = $data['libraryReading']; if ($type == 'Private') { continue; } if (!$config['ownedPublicGroupID'] && $type == 'PublicOpen' && $owner == $config['userID'] && $libraryReading == 'all') { $config['ownedPublicGroupID'] = $id; } else { if (!$config['ownedPublicNoAnonymousGroupID'] && $type == 'PublicClosed' && $owner == $config['userID'] && $libraryReading == 'members') { $config['ownedPublicNoAnonymousGroupID'] = $id; } else { $toDelete[] = $id; } } }
public function testTagNewer() { API::userClear(self::$config['userID']); // Create items with tags API::createItem("book", array("tags" => array(array("tag" => "a"), array("tag" => "b"))), $this); $version = API::getLibraryVersion(); // 'newer' shouldn't return any results $response = API::userGet(self::$config['userID'], "tags?newer={$version}"); $this->assert200($response); $this->assertNumResults(0, $response); // Create another item with tags API::createItem("book", array("tags" => array(array("tag" => "a"), array("tag" => "c"))), $this); // 'newer' should return new tag (Atom) $response = API::userGet(self::$config['userID'], "tags?content=json&newer={$version}"); $this->assert200($response); $this->assertNumResults(1, $response); $this->assertGreaterThan($version, $response->getHeader('Last-Modified-Version')); $xml = API::getXMLFromResponse($response); $data = API::parseDataFromAtomEntry($xml); $data = json_decode($data['content'], true); $this->assertEquals("c", $data['tag']); $this->assertEquals(0, $data['type']); // 'newer' should return new tag (JSON) $response = API::userGet(self::$config['userID'], "tags?newer={$version}"); $this->assert200($response); $this->assertNumResults(1, $response); $this->assertGreaterThan($version, $response->getHeader('Last-Modified-Version')); $json = API::getJSONFromResponse($response)[0]; $this->assertEquals("c", $json['tag']); $this->assertEquals(0, $json['meta']['type']); }
public function testAddFileClientV5Zip() { API::userClear(self::$config['userID']); // Get last storage sync $response = API::userGet( self::$config['userID'], "laststoragesync" ); $this->assert404($response); $json = API::createItem("book", false, $this, 'jsonData'); $key = $json['key']; $json = API::createAttachmentItem("imported_url", [], $key, $this, 'jsonData'); $key = $json['key']; $version = $json['version']; $fileContents = self::getRandomUnicodeString(); $contentType = "text/html"; $charset = "utf-8"; $filename = "file.html"; $mtime = time(); $hash = md5($fileContents); // Create ZIP file $zip = new \ZipArchive(); $file = "work/$key.zip"; if ($zip->open($file, \ZIPARCHIVE::CREATE) !== TRUE) { throw new Exception("Cannot open ZIP file"); } $zip->addFromString($filename, $fileContents); $zip->addFromString("file.css", self::getRandomUnicodeString()); $zip->close(); $zipHash = md5_file($file); $zipFilename = $key . ".zip"; $zipSize = filesize($file); $zipFileContents = file_get_contents($file); // 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 upload authorization // $response = API::userPost( self::$config['userID'], "items/$key/file", $this->implodeParams([ "md5" => $hash, "mtime" => $mtime, "filename" => $filename, "filesize" => $zipSize, "zipMD5" => $zipHash, "zipFilename" => $zipFilename, "contentType" => $contentType, "charset" => $charset ]), [ "Content-Type: application/x-www-form-urlencoded", "If-None-Match: *" ] ); $this->assert200($response); $json = API::getJSONFromResponse($response); self::$toDelete[] = "$zipHash"; // Upload to S3 $response = HTTP::post( $json['url'], $json['prefix'] . $zipFileContents . $json['suffix'], [ "Content-Type: {$json['contentType']}" ] ); $this->assert201($response); // // Register upload // // If-Match with file hash shouldn't match unregistered file $response = API::userPost( self::$config['userID'], "items/$key/file", "upload=" . $json['uploadKey'], [ "Content-Type: application/x-www-form-urlencoded", "If-Match: $hash" ] ); $this->assert412($response); // If-Match with ZIP hash shouldn't match unregistered file $response = API::userPost( self::$config['userID'], "items/$key/file", "upload=" . $json['uploadKey'], [ "Content-Type: application/x-www-form-urlencoded", "If-Match: $zipHash" ] ); $this->assert412($response); $response = API::userPost( self::$config['userID'], "items/$key/file", "upload=" . $json['uploadKey'], [ "Content-Type: application/x-www-form-urlencoded", "If-None-Match: *" ] ); $this->assert204($response); // Verify attachment item metadata $response = API::userGet( self::$config['userID'], "items/$key" ); $json = API::getJSONFromResponse($response)['data']; $this->assertEquals($hash, $json['md5']); $this->assertEquals($mtime, $json['mtime']); $this->assertEquals($filename, $json['filename']); $this->assertEquals($contentType, $json['contentType']); $this->assertEquals($charset, $json['charset']); $response = API::userGet( self::$config['userID'], "laststoragesync" ); $this->assert200($response); $this->assertRegExp('/^[0-9]{10}$/', $response->getBody()); // File exists $response = API::userPost( self::$config['userID'], "items/$key/file", $this->implodeParams([ "md5" => $hash, "mtime" => $mtime + 1000, "filename" => $filename, "filesize" => $zipSize, "zip" => 1, "zipMD5" => $zipHash, "zipFilename" => $zipFilename ]), [ "Content-Type: application/x-www-form-urlencoded", "If-Match: $hash" ] ); $this->assert200($response); $json = API::getJSONFromResponse($response); $this->assertArrayHasKey("exists", $json); // Get attachment via classic sync $sessionID = Sync::login(); $xml = Sync::updated($sessionID, 2); $this->assertEquals(1, $xml->updated[0]->items->count()); $itemXML = $xml->xpath("//updated/items/item[@key='$key']")[0]; $this->assertEquals($contentType, (string) $itemXML['mimeType']); $this->assertEquals($charset, (string) $itemXML['charset']); $this->assertEquals($hash, (string) $itemXML['storageHash']); $this->assertEquals($mtime + 1000, (string) $itemXML['storageModTime']); Sync::logout($sessionID); }
public function testRelatedItemRelationsSingleRequest() { $uriPrefix = "http://zotero.org/users/" . self::$config['userID'] . "/items/"; // TEMP: Use autoloader require_once '../../model/ID.inc.php'; $item1Key = \Zotero_ID::getKey(); $item2Key = \Zotero_ID::getKey(); $item1URI = $uriPrefix . $item1Key; $item2URI = $uriPrefix . $item2Key; $item1JSON = API::getItemTemplate('book'); $item1JSON->key = $item1Key; $item1JSON->version = 0; $item1JSON->relations->{'dc:relation'} = $item2URI; $item2JSON = API::getItemTemplate('book'); $item2JSON->key = $item2Key; $item2JSON->version = 0; $response = API::postItems([$item1JSON, $item2JSON]); $this->assert200($response); $json = API::getJSONFromResponse($response); // Make sure it exists on item 1 $json = API::getItem($item1JSON->key, $this, 'json')['data']; $this->assertCount(1, $json['relations']); $this->assertEquals($item2URI, $json['relations']['dc:relation']); // And item 2, since related items are bidirectional $json = API::getItem($item2JSON->key, $this, 'json')['data']; $this->assertCount(1, $json['relations']); $this->assertEquals($item1URI, $json['relations']['dc:relation']); }
private function _testPartialWriteFailureWithUnchanged($objectType) { API::userClear(self::$config['userID']); $objectTypePlural = API::getPluralObjectType($objectType); switch ($objectType) { case 'collection': $json1 = API::createCollection("Test", false, $this, 'jsonData'); $json2 = array("name" => str_repeat("1234567890", 6554)); $json3 = array("name" => "Test"); break; case 'item': $json1 = API::createItem("book", array("title" => "Title"), $this, 'jsonData'); $json2 = API::getItemTemplate('book'); $json3 = clone $json2; $json2->title = str_repeat("1234567890", 6554); break; case 'search': $conditions = array(array('condition' => 'title', 'operator' => 'contains', 'value' => 'value')); $json1 = API::createSearch("Name", $conditions, $this, 'jsonData'); $json2 = array("name" => str_repeat("1234567890", 6554), "conditions" => $conditions); $json3 = array("name" => "Test", "conditions" => $conditions); break; } $response = API::userPost(self::$config['userID'], "{$objectTypePlural}", json_encode([$json1, $json2, $json3]), array("Content-Type: application/json")); $this->assert200($response); $json = API::getJSONFromResponse($response); $this->assertUnchangedForObject($response, 0); $this->assert400ForObject($response, false, 1); $this->assert200ForObject($response, false, 2); $json = API::getJSONFromResponse($response); $response = API::userGet(self::$config['userID'], "{$objectTypePlural}?format=keys&key=" . self::$config['apiKey']); $this->assert200($response); $keys = explode("\n", trim($response->getBody())); $this->assertCount(2, $keys); foreach ($json['success'] as $key) { $this->assertContains($key, $keys); } }
public function testEditMultipleSearches() { $search1Name = "Test 1"; $search1Conditions = [["condition" => "title", "operator" => "contains", "value" => "test"]]; $search1Data = API::createSearch($search1Name, $search1Conditions, $this, 'jsonData'); $search1NewName = "Test 1 Modified"; $search2Name = "Test 2"; $search2Conditions = [["condition" => "title", "operator" => "is", "value" => "test2"]]; $search2Data = API::createSearch($search2Name, $search2Conditions, $this, 'jsonData'); $search2NewConditions = [["condition" => "title", "operator" => "isNot", "value" => "test1"]]; $response = API::userPost(self::$config['userID'], "searches", json_encode([['key' => $search1Data['key'], 'version' => $search1Data['version'], 'name' => $search1NewName], ['key' => $search2Data['key'], 'version' => $search2Data['version'], 'conditions' => $search2NewConditions]]), ["Content-Type: application/json"]); $this->assert200($response); $libraryVersion = $response->getHeader("Last-Modified-Version"); $json = API::getJSONFromResponse($response); $this->assertCount(2, $json['successful']); // Deprecated $this->assertCount(2, $json['success']); // Check data in write response $this->assertEquals($json['successful'][0]['key'], $json['successful'][0]['data']['key']); $this->assertEquals($json['successful'][1]['key'], $json['successful'][1]['data']['key']); $this->assertEquals($libraryVersion, $json['successful'][0]['version']); $this->assertEquals($libraryVersion, $json['successful'][1]['version']); $this->assertEquals($libraryVersion, $json['successful'][0]['data']['version']); $this->assertEquals($libraryVersion, $json['successful'][1]['data']['version']); $this->assertEquals($search1NewName, $json['successful'][0]['data']['name']); $this->assertEquals($search2Name, $json['successful'][1]['data']['name']); $this->assertEquals($search1Conditions, $json['successful'][0]['data']['conditions']); $this->assertEquals($search2NewConditions, $json['successful'][1]['data']['conditions']); // Check in separate request, to be safe $keys = array_map(function ($o) { return $o['key']; }, $json['successful']); $response = API::getSearchResponse($keys); $this->assertTotalResults(2, $response); $json = API::getJSONFromResponse($response); // POST follows PATCH behavior, so unspecified values shouldn't change $this->assertEquals($search1NewName, $json[0]['data']['name']); $this->assertEquals($search1Conditions, $json[0]['data']['conditions']); $this->assertEquals($search2Name, $json[1]['data']['name']); $this->assertEquals($search2NewConditions, $json[1]['data']['conditions']); }
public function testSaveUnchangedSanitizedNote() { $json = API::createNoteItem("<span >Foo</span>", false, $this, 'json'); $response = API::postItem($json['data']); $json = API::getJSONFromResponse($response); $this->assertArrayHasKey(0, $json['unchanged']); }
public function testSortDirection() { API::userClear(self::$config['userID']); // Setup $dataArray = []; $dataArray[] = API::createItem("book", ['title' => "B", 'creators' => [["creatorType" => "author", "name" => "B"]], 'dateAdded' => '2014-02-05T00:00:00Z', 'dateModified' => '2014-04-05T01:00:00Z'], $this, 'jsonData'); $dataArray[] = API::createItem("journalArticle", ['title' => "A", 'creators' => [["creatorType" => "author", "name" => "A"]], 'dateAdded' => '2014-02-04T00:00:00Z', 'dateModified' => '2014-01-04T01:00:00Z'], $this, 'jsonData'); $dataArray[] = API::createItem("newspaperArticle", ['title' => "F", 'creators' => [["creatorType" => "author", "name" => "F"]], 'dateAdded' => '2014-02-03T00:00:00Z', 'dateModified' => '2014-02-03T01:00:00Z'], $this, 'jsonData'); $dataArray[] = API::createItem("book", ['title' => "C", 'creators' => [["creatorType" => "author", "name" => "C"]], 'dateAdded' => '2014-02-02T00:00:00Z', 'dateModified' => '2014-03-02T01:00:00Z'], $this, 'jsonData'); // Get sorted keys usort($dataArray, function ($a, $b) { return strcmp($a['dateAdded'], $b['dateAdded']); }); $keysByDateAddedAscending = array_map(function ($data) { return $data['key']; }, $dataArray); $keysByDateAddedDescending = array_reverse($keysByDateAddedAscending); // Ascending $response = API::userGet(self::$config['userID'], "items?format=keys&sort=dateAdded&direction=asc"); $this->assert200($response); $this->assertEquals($keysByDateAddedAscending, explode("\n", trim($response->getBody()))); $response = API::userGet(self::$config['userID'], "items?format=json&sort=dateAdded&direction=asc"); $this->assert200($response); $json = API::getJSONFromResponse($response); $keys = array_map(function ($val) { return $val['key']; }, $json); $this->assertEquals($keysByDateAddedAscending, $keys); $response = API::userGet(self::$config['userID'], "items?format=atom&sort=dateAdded&direction=asc"); $this->assert200($response); $xml = API::getXMLFromResponse($response); $keys = array_map(function ($val) { return (string) $val; }, $xml->xpath('//atom:entry/zapi:key')); $this->assertEquals($keysByDateAddedAscending, $keys); // Ascending using old 'order'/'sort' instead of 'sort'/'direction' $response = API::userGet(self::$config['userID'], "items?format=keys&order=dateAdded&sort=asc"); $this->assert200($response); $this->assertEquals($keysByDateAddedAscending, explode("\n", trim($response->getBody()))); $response = API::userGet(self::$config['userID'], "items?format=json&order=dateAdded&sort=asc"); $this->assert200($response); $json = API::getJSONFromResponse($response); $keys = array_map(function ($val) { return $val['key']; }, $json); $this->assertEquals($keysByDateAddedAscending, $keys); $response = API::userGet(self::$config['userID'], "items?format=atom&order=dateAdded&sort=asc"); $this->assert200($response); $xml = API::getXMLFromResponse($response); $keys = array_map(function ($val) { return (string) $val; }, $xml->xpath('//atom:entry/zapi:key')); $this->assertEquals($keysByDateAddedAscending, $keys); // Deprecated 'order'/'sort', but the wrong way $response = API::userGet(self::$config['userID'], "items?format=keys&sort=dateAdded&order=asc"); $this->assert200($response); $this->assertEquals($keysByDateAddedAscending, explode("\n", trim($response->getBody()))); // Descending $response = API::userGet(self::$config['userID'], "items?format=keys&sort=dateAdded&direction=desc"); $this->assert200($response); $this->assertEquals($keysByDateAddedDescending, explode("\n", trim($response->getBody()))); $response = API::userGet(self::$config['userID'], "items?format=json&sort=dateAdded&direction=desc"); $this->assert200($response); $json = API::getJSONFromResponse($response); $keys = array_map(function ($val) { return $val['key']; }, $json); $this->assertEquals($keysByDateAddedDescending, $keys); $response = API::userGet(self::$config['userID'], "items?format=atom&sort=dateAdded&direction=desc"); $this->assert200($response); $xml = API::getXMLFromResponse($response); $keys = array_map(function ($val) { return (string) $val; }, $xml->xpath('//atom:entry/zapi:key')); $this->assertEquals($keysByDateAddedDescending, $keys); // Descending $response = API::userGet(self::$config['userID'], "items?format=keys&order=dateAdded&sort=desc"); $this->assert200($response); $this->assertEquals($keysByDateAddedDescending, explode("\n", trim($response->getBody()))); $response = API::userGet(self::$config['userID'], "items?format=json&order=dateAdded&sort=desc"); $this->assert200($response); $json = API::getJSONFromResponse($response); $keys = array_map(function ($val) { return $val['key']; }, $json); $this->assertEquals($keysByDateAddedDescending, $keys); $response = API::userGet(self::$config['userID'], "items?format=atom&order=dateAdded&sort=desc"); $this->assert200($response); $xml = API::getXMLFromResponse($response); $keys = array_map(function ($val) { return (string) $val; }, $xml->xpath('//atom:entry/zapi:key')); $this->assertEquals($keysByDateAddedDescending, $keys); }
public function testEditMultipleCollections() { $collection1Data = API::createCollection("Test 1", false, $this, 'jsonData'); $collection2Name = "Test 2"; $collection2Data = API::createCollection($collection2Name, false, $this, 'jsonData'); $collection1NewName = "Test 1 Modified"; $collection2NewParentKey = API::createCollection("Test 3", false, $this, 'key'); $response = API::userPost(self::$config['userID'], "collections", json_encode([['key' => $collection1Data['key'], 'version' => $collection1Data['version'], 'name' => $collection1NewName], ['key' => $collection2Data['key'], 'version' => $collection2Data['version'], 'parentCollection' => $collection2NewParentKey]]), ["Content-Type: application/json"]); $this->assert200($response); $libraryVersion = $response->getHeader("Last-Modified-Version"); $json = API::getJSONFromResponse($response); $this->assertCount(2, $json['successful']); // Deprecated $this->assertCount(2, $json['success']); // Check data in write response $this->assertEquals($json['successful'][0]['key'], $json['successful'][0]['data']['key']); $this->assertEquals($json['successful'][1]['key'], $json['successful'][1]['data']['key']); $this->assertEquals($libraryVersion, $json['successful'][0]['version']); $this->assertEquals($libraryVersion, $json['successful'][1]['version']); $this->assertEquals($libraryVersion, $json['successful'][0]['data']['version']); $this->assertEquals($libraryVersion, $json['successful'][1]['data']['version']); $this->assertEquals($collection1NewName, $json['successful'][0]['data']['name']); $this->assertEquals($collection2Name, $json['successful'][1]['data']['name']); $this->assertFalse($json['successful'][0]['data']['parentCollection']); $this->assertEquals($collection2NewParentKey, $json['successful'][1]['data']['parentCollection']); // Check in separate request, to be safe $keys = array_map(function ($o) { return $o['key']; }, $json['successful']); $response = API::getCollectionResponse($keys); $this->assertTotalResults(2, $response); $json = API::getJSONFromResponse($response); // POST follows PATCH behavior, so unspecified values shouldn't change $this->assertEquals($collection1NewName, $json[0]['data']['name']); $this->assertFalse($json[0]['data']['parentCollection']); $this->assertEquals($collection2Name, $json[1]['data']['name']); $this->assertEquals($collection2NewParentKey, $json[1]['data']['parentCollection']); }
public function testEditMultipleSearches() { $search1Name = "Test 1"; $search1Conditions = [["condition" => "title", "operator" => "contains", "value" => "test"]]; $search1Data = API::createSearch($search1Name, $search1Conditions, $this, 'jsonData'); $search1NewName = "Test 1 Modified"; $search2Name = "Test 2"; $search2Conditions = [["condition" => "title", "operator" => "is", "value" => "test2"]]; $search2Data = API::createSearch($search2Name, $search2Conditions, $this, 'jsonData'); $search2NewConditions = [["condition" => "title", "operator" => "isNot", "value" => "test1"]]; $response = API::userPost(self::$config['userID'], "searches", json_encode([['key' => $search1Data['key'], 'version' => $search1Data['version'], 'name' => $search1NewName], ['key' => $search2Data['key'], 'version' => $search2Data['version'], 'conditions' => $search2NewConditions]]), ["Content-Type: application/json"]); $this->assert200($response); $json = API::getJSONFromResponse($response); $this->assertCount(2, $json['success']); $response = API::getSearchResponse($json['success']); $this->assertTotalResults(2, $response); $json = API::getJSONFromResponse($response); // POST follows PATCH behavior, so unspecified values shouldn't change $this->assertEquals($search1NewName, $json[0]['data']['name']); $this->assertEquals($search1Conditions, $json[0]['data']['conditions']); $this->assertEquals($search2Name, $json[1]['data']['name']); $this->assertEquals($search2NewConditions, $json[1]['data']['conditions']); }
private function _testSinceContent($param) { API::userClear(self::$config['userID']); // Store content for one item $key = API::createItem("book", false, $this, 'key'); $json = API::createAttachmentItem("imported_url", [], $key, $this, 'jsonData'); $key1 = $json['key']; $content = "Here is some full-text content"; $response = API::userPut(self::$config['userID'], "items/{$key1}/fulltext", json_encode(["content" => $content]), array("Content-Type: application/json")); $this->assert204($response); $contentVersion1 = $response->getHeader("Last-Modified-Version"); $this->assertGreaterThan(0, $contentVersion1); // And another $key = API::createItem("book", false, $this, 'key'); $json = API::createAttachmentItem("imported_url", [], $key, $this, 'jsonData'); $key2 = $json['key']; $response = API::userPut(self::$config['userID'], "items/{$key2}/fulltext", json_encode(["content" => $content]), array("Content-Type: application/json")); $this->assert204($response); $contentVersion2 = $response->getHeader("Last-Modified-Version"); $this->assertGreaterThan(0, $contentVersion2); // Get newer one $response = API::userGet(self::$config['userID'], "fulltext?{$param}={$contentVersion1}"); $this->assert200($response); $this->assertContentType("application/json", $response); $this->assertEquals($contentVersion2, $response->getHeader("Last-Modified-Version")); $json = API::getJSONFromResponse($response); $this->assertCount(1, $json); $this->assertArrayHasKey($key2, $json); $this->assertEquals($contentVersion2, $json[$key2]); // Get both with since=0 $response = API::userGet(self::$config['userID'], "fulltext?{$param}=0"); $this->assert200($response); $this->assertContentType("application/json", $response); $json = API::getJSONFromResponse($response); $this->assertCount(2, $json); $this->assertArrayHasKey($key1, $json); $this->assertEquals($contentVersion1, $json[$key1]); $this->assertArrayHasKey($key1, $json); $this->assertEquals($contentVersion2, $json[$key2]); }
public function testPatchItems() { // Create top-level item API::useAPIKey(self::$config['apiKey']); $json = API::getItemTemplate("book"); $response = API::userPost(self::$config['userID'], "publications/items", json_encode([$json])); $this->assert200($response); $key = API::getJSONFromResponse($response)['successful'][0]['key']; $version = $response->getHeader("Last-Modified-Version"); $json = ["key" => $key, "version" => $version, "title" => "Test"]; $response = API::userPost(self::$config['userID'], "publications/items", json_encode([$json]), ["Content-Type: application/json"]); $this->assert200ForObject($response); }
public function testLinkedFileAttachment() { $msg = "Linked-file attachments cannot be added to publications libraries"; // Create top-level item API::useAPIKey(self::$config['apiKey']); $json = API::getItemTemplate("book"); $response = API::userPost(self::$config['userID'], "publications/items", json_encode([$json])); $this->assert200($response); $json = API::getJSONFromResponse($response); $itemKey = $json['success'][0]; $json = API::getItemTemplate("attachment&linkMode=linked_file"); $json->parentItem = $itemKey; API::useAPIKey(self::$config['apiKey']); $response = API::userPost(self::$config['userID'], "publications/items", json_encode([$json]), array("Content-Type: application/json")); $this->assert400ForObject($response, $msg, 0); }
public function testIncludeBibMulti() { $keys = array_keys(self::$items); $keyStr = implode(',', $keys); foreach (self::$styles as $style) { $response = API::userGet(self::$config['userID'], "items?itemKey={$keyStr}&include=bib" . ($style == "default" ? "" : "&style=" . urlencode($style))); $this->assert200($response); $this->assertTotalResults(sizeOf($keys), $response); $json = API::getJSONFromResponse($response); foreach ($json as $item) { $key = $item['key']; $this->assertXmlStringEqualsXmlString(self::$items[$key]['json']['bib'][$style], $item['bib']); } } }
public function testWebTranslationMultiple() { $title = 'Zotero: A Guide for Librarians, Researchers and Educators'; $response = API::userPost(self::$config['userID'], "items?key=" . self::$config['apiKey'], json_encode(["url" => "http://www.amazon.com/s/field-keywords=zotero+guide+librarians"]), array("Content-Type: application/json")); $this->assert300($response); $json = json_decode($response->getBody()); $results = get_object_vars($json->items); $key = array_keys($results)[0]; $val = array_values($results)[0]; $this->assertEquals('0', $key); $this->assertEquals($title, $val); $items = new \stdClass(); $items->{$key} = $val; // Missing token $response = API::userPost(self::$config['userID'], "items?key=" . self::$config['apiKey'], json_encode(["url" => "http://www.amazon.com/s/field-keywords=zotero+guide+librarians", "items" => $items]), array("Content-Type: application/json")); $this->assert400($response, "Token not provided with selected items"); // Invalid selection $items2 = clone $items; $invalidKey = "12345"; $items2->{$invalidKey} = $items2->{$key}; unset($items2->{$key}); $response = API::userPost(self::$config['userID'], "items?key=" . self::$config['apiKey'], json_encode(["url" => "http://www.amazon.com/s/field-keywords=zotero+guide+librarians", "token" => $json->token, "items" => $items2]), array("Content-Type: application/json")); $this->assert400($response, "Index '{$invalidKey}' not found for URL and token"); $response = API::userPost(self::$config['userID'], "items?key=" . self::$config['apiKey'], json_encode(["url" => "http://www.amazon.com/s/field-keywords=zotero+guide+librarians", "token" => $json->token, "items" => $items]), array("Content-Type: application/json")); $this->assert200($response); $this->assert200ForObject($response); $json = API::getJSONFromResponse($response); $itemKey = $json['success'][0]; $data = API::getItem($itemKey, $this, 'json')['data']; $this->assertEquals($title, $data['title']); }
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); $json = API::createItem("book", false, $this, 'jsonData'); $key = $json['key']; $fileContentType = "text/html"; $fileCharset = "UTF-8"; $fileFilename = "file.html"; $fileModtime = time(); $json = API::createAttachmentItem("imported_url", [], $key, $this, 'jsonData'); $key = $json['key']; $version = $json['version']; $json['contentType'] = $fileContentType; $json['charset'] = $fileCharset; $json['filename'] = $fileFilename; $response = API::userPut(self::$config['userID'], "items/{$key}", 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/{$json['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/{$json['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/{$json['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/{$json['key']}"); $json = API::getJSONFromResponse($response)['data']; $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/{$json['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()); }
protected function assertNoResults($response) { $this->assertTotalResults(0, $response); $contentType = $response->getHeader('Content-Type'); if ($contentType == 'application/json') { $json = API::getJSONFromResponse($response); $this->assertEquals(0, count($json)); } else { if ($contentType == 'application/atom+xml') { $xml = new SimpleXMLElement($response->getBody()); $zapiNodes = $xml->children(self::$nsZAPI); $this->assertEquals(0, count($xml->entry)); } else { throw new Exception("Unknown content type '{$contentType}'"); } } }
public function testItemQuickSearchOrderByDate() { $title1 = "Test Title"; $title2 = "Another Title"; $keys = []; $keys[] = API::createItem("book", ['title' => $title1, 'date' => "February 12, 2013"], $this, 'key'); $keys[] = API::createItem("journalArticle", ['title' => $title2, 'date' => "November 25, 2012"], $this, 'key'); // Search for one by title $response = API::userGet(self::$config['userID'], "items?q=" . urlencode($title1)); $this->assert200($response); $this->assertNumResults(1, $response); $json = API::getJSONFromResponse($response); $this->assertEquals($keys[0], $json[0]['key']); // Search by both by title, date asc $response = API::userGet(self::$config['userID'], "items?q=title&sort=date&direction=asc"); $this->assert200($response); $this->assertNumResults(2, $response); $json = API::getJSONFromResponse($response); $this->assertEquals($keys[1], $json[0]['key']); $this->assertEquals($keys[0], $json[1]['key']); // Search by both by title, date asc, with old-style parameters $response = API::userGet(self::$config['userID'], "items?q=title&order=date&sort=asc"); $this->assert200($response); $this->assertNumResults(2, $response); $json = API::getJSONFromResponse($response); $this->assertEquals($keys[1], $json[0]['key']); $this->assertEquals($keys[0], $json[1]['key']); // Search by both by title, date desc $response = API::userGet(self::$config['userID'], "items?q=title&sort=date&direction=desc"); $this->assert200($response); $this->assertNumResults(2, $response); $json = API::getJSONFromResponse($response); $this->assertEquals($keys[0], $json[0]['key']); $this->assertEquals($keys[1], $json[1]['key']); // Search by both by title, date desc, with old-style parameters $response = API::userGet(self::$config['userID'], "items?q=title&order=date&sort=desc"); $this->assert200($response); $this->assertNumResults(2, $response); $json = API::getJSONFromResponse($response); $this->assertEquals($keys[0], $json[0]['key']); $this->assertEquals($keys[1], $json[1]['key']); }
public function testKeyCreateAndDelete() { API::useAPIKey(""); $name = "Test " . uniqid(); // Can't create as user $response = API::userPost(self::$config['userID'], 'keys', json_encode(['name' => $name, 'access' => ['user' => ['library' => true]]])); $this->assert403($response); // Create as root $response = API::userPost(self::$config['userID'], 'keys', json_encode(['name' => $name, 'access' => ['user' => ['library' => true]]]), [], ["username" => self::$config['rootUsername'], "password" => self::$config['rootPassword']]); $this->assert201($response); $json = API::getJSONFromResponse($response); $key = $json['key']; $this->assertEquals($json['name'], $name); $this->assertEquals(['user' => ['library' => true, 'files' => true]], $json['access']); // Delete anonymously (with embedded key) $response = API::userDelete(self::$config['userID'], "keys/{$key}"); $this->assert204($response); $response = API::userGet(self::$config['userID'], "keys/{$key}"); $this->assert404($response); }
/** * Changing a group's metadata should change its ETag */ public function testUpdateMetadataJSON() { $response = API::userGet(self::$config['userID'], "groups"); $this->assert200($response); // Get group API URI and ETag $json = API::getJSONFromResponse($response)[0]; $groupID = $json['id']; $url = $json['links']['self']['href']; $url = str_replace(self::$config['apiURLPrefix'], '', $url); $version = $json['version']; //$etag = (string) array_shift($xml->xpath("//atom:entry/atom:content/@etag")); // Make sure format=versions returns the same version $response = API::userGet(self::$config['userID'], "groups?format=versions&key=" . self::$config['apiKey']); $this->assert200($response); $this->assertEquals($version, json_decode($response->getBody())->{$groupID}); // Update group metadata $xml = new SimpleXMLElement("<group/>"); foreach ($json['data'] as $key => $val) { switch ($key) { case 'id': case 'version': case 'members': continue; case 'name': $name = "My Test Group " . uniqid(); $xml['name'] = $name; break; case 'description': $description = "This is a test description " . uniqid(); $xml->{$key} = $description; break; case 'url': $urlField = "http://example.com/" . uniqid(); $xml->{$key} = $urlField; break; default: $xml[$key] = $val; } } $xml = trim(preg_replace('/^<\\?xml.+\\n/', "", $xml->asXML())); $response = API::put($url, $xml, array("Content-Type: text/xml"), array("username" => self::$config['rootUsername'], "password" => self::$config['rootPassword'])); $this->assert200($response); $xml = API::getXMLFromResponse($response); $xml->registerXPathNamespace('zxfer', 'http://zotero.org/ns/transfer'); $group = $xml->xpath('//atom:entry/atom:content/zxfer:group'); $this->assertCount(1, $group); $this->assertEquals($name, $group[0]['name']); $response = API::userGet(self::$config['userID'], "groups?format=versions&key=" . self::$config['apiKey']); $this->assert200($response); $json = json_decode($response->getBody()); $newVersion = $json->{$groupID}; $this->assertNotEquals($version, $newVersion); // Check version header on individual group request $response = API::groupGet($groupID, ""); $this->assert200($response); $this->assertEquals($newVersion, $response->getHeader('Last-Modified-Version')); $json = API::getJSONFromResponse($response)['data']; $this->assertEquals($name, $json['name']); $this->assertEquals($description, $json['description']); $this->assertEquals($urlField, $json['url']); }
private function _testMultiObjectLastModifiedVersion($objectType) { $objectTypePlural = API::getPluralObjectType($objectType); $response = API::userGet(self::$config['userID'], "{$objectTypePlural}?limit=1"); $version = $response->getHeader("Last-Modified-Version"); $this->assertTrue(is_numeric($version)); switch ($objectType) { case 'collection': $json = new \stdClass(); $json->name = "Name"; break; case 'item': $json = API::getItemTemplate("book"); break; case 'search': $json = new \stdClass(); $json->name = "Name"; $json->conditions = array(array("condition" => "title", "operator" => "contains", "value" => "test")); break; } // Outdated library version $response = API::userPost(self::$config['userID'], "{$objectTypePlural}", json_encode(array($objectTypePlural => array($json))), array("Content-Type: application/json", "If-Unmodified-Since-Version: " . ($version - 1))); $this->assert412($response); // Make sure version didn't change during failure $response = API::userGet(self::$config['userID'], "{$objectTypePlural}?limit=1"); $this->assertEquals($version, $response->getHeader("Last-Modified-Version")); // Create a new object, using library timestamp $response = API::userPost(self::$config['userID'], "{$objectTypePlural}", json_encode([$json]), array("Content-Type: application/json", "If-Unmodified-Since-Version: {$version}")); $this->assert200($response); $version2 = $response->getHeader("Last-Modified-Version"); $this->assertTrue(is_numeric($version2)); // Version should be incremented on new object $this->assertGreaterThan($version, $version2); $objectKey = API::getFirstSuccessKeyFromResponse($response); // Check single-object request $response = API::userGet(self::$config['userID'], "{$objectTypePlural}/{$objectKey}"); $this->assert200($response); $version = $response->getHeader("Last-Modified-Version"); $this->assertTrue(is_numeric($version)); $this->assertEquals($version, $version2); $json = API::getJSONFromResponse($response)['data']; // Modify object $json['key'] = $objectKey; switch ($objectType) { case 'collection': $json['name'] = "New Name"; break; case 'item': $json['title'] = "New Title"; break; case 'search': $json['name'] = "New Name"; break; } // No If-Unmodified-Since-Version or object version property unset($json['version']); $response = API::userPost(self::$config['userID'], "{$objectTypePlural}", json_encode([$json]), array("Content-Type: application/json")); $this->assert428ForObject($response); // Outdated object version property $json['version'] = $version - 1; $response = API::userPost(self::$config['userID'], "{$objectTypePlural}", json_encode([$json]), array("Content-Type: application/json")); $this->assert412ForObject($response, ucwords($objectType) . " has been modified since specified version " . "(expected {$json['version']}, found {$version})"); // Modify object, using object version property $json['version'] = $version; $response = API::userPost(self::$config['userID'], "{$objectTypePlural}", json_encode([$json]), array("Content-Type: application/json")); $this->assert200($response); // Version should be incremented on modified object $version3 = $response->getHeader("Last-Modified-Version"); $this->assertTrue(is_numeric($version3)); $this->assertGreaterThan($version2, $version3); // Check library version $response = API::userGet(self::$config['userID'], "{$objectTypePlural}"); $version = $response->getHeader("Last-Modified-Version"); $this->assertTrue(is_numeric($version)); $this->assertEquals($version, $version3); // Check single-object request $response = API::userGet(self::$config['userID'], "{$objectTypePlural}/{$objectKey}"); $version = $response->getHeader("Last-Modified-Version"); $this->assertTrue(is_numeric($version)); $this->assertEquals($version, $version3); // TODO: Version should be incremented on deleted item }
private function getKeysWithAllGroupAccess($userID) { $response = API::superGet("users/{$userID}/keys"); $this->assert200($response); $json = API::getJSONFromResponse($response); return array_map(function ($keyObj) { return $keyObj['key']; }, array_filter($json, function ($keyObj) { return !empty($keyObj['access']['groups']['all']['library']); })); }