Beispiel #1
0
 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]);
         }
     }
 }
Beispiel #2
0
 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);
 }
Beispiel #3
0
 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']);
 }
Beispiel #4
0
 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);
 }
Beispiel #5
0
	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);
	}
Beispiel #6
0
<?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;
        }
    }
}
Beispiel #7
0
 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']);
 }
Beispiel #8
0
	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);
	}
Beispiel #9
0
 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']);
 }
Beispiel #10
0
 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);
     }
 }
Beispiel #11
0
 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']);
 }
Beispiel #12
0
 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']);
 }
Beispiel #13
0
 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);
 }
Beispiel #14
0
 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']);
 }
Beispiel #15
0
 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']);
 }
Beispiel #16
0
 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]);
 }
Beispiel #17
0
 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);
 }
Beispiel #18
0
 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);
 }
Beispiel #19
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']);
         }
     }
 }
Beispiel #20
0
 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']);
 }
Beispiel #21
0
 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());
 }
Beispiel #22
0
 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}'");
         }
     }
 }
Beispiel #23
0
 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']);
 }
Beispiel #24
0
 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);
 }
Beispiel #25
0
 /**
  * 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']);
 }
Beispiel #26
0
 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']);
     }));
 }