/** * An object type's primary data cache for a library has to be created before * */ public function testCacheCreatorPrimaryData() { $data = array("title" => "Title", "creators" => array(array("creatorType" => "author", "firstName" => "First", "lastName" => "Last"), array("creatorType" => "editor", "firstName" => "Ed", "lastName" => "McEditor"))); $key = API::createItem("book", $data, $this, 'key'); $response = API::userGet(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'] . "&content=csljson"); $json = json_decode(API::getContentFromResponse($response)); $this->assertEquals("First", $json->author[0]->given); $this->assertEquals("Last", $json->author[0]->family); $this->assertEquals("Ed", $json->editor[0]->given); $this->assertEquals("McEditor", $json->editor[0]->family); }
/** * Changing a group's metadata should change its ETag */ public function testUpdateMetadata() { $response = API::userGet(self::$config['userID'], "groups?content=json&key=" . self::$config['apiKey']); $this->assert200($response); // Get group API URI and ETag $xml = API::getXMLFromResponse($response); $xml->registerXPathNamespace('atom', 'http://www.w3.org/2005/Atom'); $xml->registerXPathNamespace('zapi', 'http://zotero.org/ns/api'); $groupID = (string) array_shift($xml->xpath("//atom:entry/zapi:groupID")); $url = (string) array_shift($xml->xpath("//atom:entry/atom:link[@rel='self']/@href")); $url = str_replace(self::$config['apiURLPrefix'], '', $url); $etag = (string) array_shift($xml->xpath("//atom:entry/atom:content/@etag")); // Make sure format=etags returns the same ETag $response = API::userGet(self::$config['userID'], "groups?format=etags&key=" . self::$config['apiKey']); $this->assert200($response); $json = json_decode($response->getBody()); $this->assertEquals($etag, $json->{$groupID}); // Update group metadata $json = json_decode(array_shift($xml->xpath("//atom:entry/atom:content"))); $xml = new SimpleXMLElement("<group/>"); foreach ($json as $key => $val) { switch ($key) { case 'id': 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=etags&key=" . self::$config['apiKey']); $this->assert200($response); $json = json_decode($response->getBody()); $newETag = $json->{$groupID}; $this->assertNotEquals($etag, $newETag); // Check ETag header on individual group request $response = API::groupGet($groupID, "?content=json&key=" . self::$config['apiKey']); $this->assert200($response); $this->assertEquals($newETag, $response->getHeader('ETag')); $json = json_decode(API::getContentFromResponse($response)); $this->assertEquals($name, $json->name); $this->assertEquals($description, $json->description); $this->assertEquals($urlField, $json->url); }
public function testReverseSameAs() { $items = array(); $item = ["key" => API::createItem("book", false, null, 'key'), "relations" => [["owl:sameAs", "http://zotero.org/groups/1/items/AAAAAAAA"]]]; $xml = Sync::updated(self::$sessionID); $updateKey = $xml['updateKey']; $lastSyncTimestamp = $xml['timestamp']; $xmlstr = '<data version="9">' . '<relations>'; $subject = 'http://zotero.org/users/' . self::$config['userID'] . '/items/' . $item['key']; // Insert backwards, as client does via classic sync // if group item is dragged to personal library foreach ($item['relations'] as $rel) { $xmlstr .= '<relation libraryID="' . self::$config['libraryID'] . '">' . "<subject>{$rel[1]}</subject>" . "<predicate>{$rel[0]}</predicate>" . "<object>{$subject}</object>" . '</relation>'; } $xmlstr .= '</relations>' . '</data>'; $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr); Sync::waitForUpload(self::$sessionID, $response, $this); // Check via API $response = API::userGet(self::$config['userID'], "items/{$item['key']}?key=" . self::$config['apiKey'] . "&content=json"); $content = API::getContentFromResponse($response); $json = json_decode($content, true); $uniquePredicates = array_unique(array_map(function ($x) { return $x[0]; }, $item['relations'])); $this->assertCount(sizeOf($uniquePredicates), $json['relations']); foreach ($item['relations'] as $rel) { $this->assertArrayHasKey($rel[0], $json['relations']); $this->assertContains($rel[1], $json['relations'][$rel[0]]); } // PUT via API, which should be unchanged $response = API::userPost(self::$config['userID'], "items?key=" . self::$config['apiKey'], json_encode(["items" => [$json]])); $results = json_decode($response->getBody(), true); $this->assertArrayHasKey('unchanged', $results); $this->assertContains($item['key'], $results['unchanged']); // Add another owl:sameAs via API if (is_string($json['relations']['owl:sameAs'])) { $json['relations']['owl:sameAs'] = [$json['relations']['owl:sameAs']]; } $newURI = "http://zotero.org/groups/1/items/BBBBBBBB"; $json['relations']['owl:sameAs'][] = $newURI; $item['relations'][] = ['owl:sameAs', $newURI]; $response = API::userPost(self::$config['userID'], "items?key=" . self::$config['apiKey'], json_encode(["items" => [$json]])); $this->assertEquals(200, $response->getStatus()); $results = json_decode($response->getBody(), true); $this->assertArrayHasKey('success', $results); $this->assertContains($item['key'], $results['success']); // Check via API $response = API::userGet(self::$config['userID'], "items/{$item['key']}?key=" . self::$config['apiKey'] . "&content=json"); $content = API::getContentFromResponse($response); $json = json_decode($content, true); $uniquePredicates = array_unique(array_map(function ($x) { return $x[0]; }, $item['relations'])); $this->assertCount(sizeOf($uniquePredicates), $json['relations']); foreach ($item['relations'] as $rel) { $this->assertArrayHasKey($rel[0], $json['relations']); $this->assertContains($rel[1], $json['relations'][$rel[0]]); } $this->assertArrayHasKey("owl:sameAs", $json['relations']); $this->assertContains($newURI, $json['relations']['owl:sameAs']); $xml = Sync::updated(self::$sessionID); $updateKey = $xml['updateKey']; // First URL should still be in reverse order $this->assertEquals(2, sizeOf($xml->updated[0]->relations->xpath("//relations/relation"))); $subRel = $xml->updated[0]->relations->xpath("//relations/relation[subject/text() = '{$item['relations'][0][1]}']"); $objRel = $xml->updated[0]->relations->xpath("//relations/relation[object/text() = '{$newURI}']"); $this->assertEquals(1, sizeOf($subRel)); $this->assertEquals($subject, $subRel[0]->object); $this->assertEquals(1, sizeOf($objRel)); $this->assertEquals($subject, $objRel[0]->subject); // Resave second relation via classic sync in reverse order $xmlstr = '<data version="9"><relations>'; $xmlstr .= '<relation libraryID="' . self::$config['libraryID'] . '">' . "<subject>{$newURI}</subject>" . "<predicate>owl:sameAs</predicate>" . "<object>{$subject}</object>" . "</relation></relations></data>"; $response = Sync::upload(self::$sessionID, $updateKey, $xmlstr); Sync::waitForUpload(self::$sessionID, $response, $this); $xml = Sync::updated(self::$sessionID); $this->assertEquals(2, sizeOf($xml->updated[0]->relations->xpath("//relations/relation"))); // Delete reverse relation via API $response = API::userGet(self::$config['userID'], "items/{$item['key']}?key=" . self::$config['apiKey'] . "&content=json"); $content = API::getContentFromResponse($response); $json = json_decode($content, true); // Leave just the relation that's entered in normal order $json['relations']['owl:sameAs'] = [$newURI]; $item['relations'] = ['owl:sameAs', $newURI]; $response = API::userPost(self::$config['userID'], "items?key=" . self::$config['apiKey'], json_encode(["items" => [$json]])); $this->assertEquals(200, $response->getStatus()); $results = json_decode($response->getBody(), true); $this->assertArrayHasKey('success', $results); $this->assertContains($item['key'], $results['success']); $response = API::userGet(self::$config['userID'], "items/{$item['key']}?key=" . self::$config['apiKey'] . "&content=json"); $content = API::getContentFromResponse($response); $json = json_decode($content, true); $this->assertArrayHasKey("owl:sameAs", $json['relations']); $this->assertContains($newURI, $json['relations']['owl:sameAs']); // Should only have one relation left $this->assertEquals(1, sizeOf($json['relations']['owl:sameAs'])); }
private function _testMultiObjectLastModifiedVersion($objectType) { $objectTypePlural = API::getPluralObjectType($objectType); $objectKeyProp = $objectType . "Key"; $objectVersionProp = $objectType . "Version"; $response = API::userGet(self::$config['userID'], "{$objectTypePlural}?key=" . self::$config['apiKey'] . "&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}?key=" . self::$config['apiKey'], 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}?key=" . self::$config['apiKey'] . "&limit=1"); $this->assertEquals($version, $response->getHeader("Last-Modified-Version")); // Create a new object, using library timestamp $response = API::userPost(self::$config['userID'], "{$objectTypePlural}?key=" . self::$config['apiKey'], json_encode(array($objectTypePlural => array($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}?key=" . self::$config['apiKey'] . "&content=json"); $this->assert200($response); $version = $response->getHeader("Last-Modified-Version"); $this->assertTrue(is_numeric($version)); $this->assertEquals($version, $version2); $json = json_decode(API::getContentFromResponse($response)); // Modify object $json->{$objectKeyProp} = $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->{$objectVersionProp}); $response = API::userPost(self::$config['userID'], "{$objectTypePlural}?key=" . self::$config['apiKey'], json_encode(array($objectTypePlural => array($json))), array("Content-Type: application/json")); $this->assert428ForObject($response); // Outdated object version property $json->{$objectVersionProp} = $version - 1; $response = API::userPost(self::$config['userID'], "{$objectTypePlural}?key=" . self::$config['apiKey'], json_encode(array($objectTypePlural => array($json))), array("Content-Type: application/json")); $this->assert412ForObject($response, ucwords($objectType) . " has been modified since specified version " . "(expected {$json->{$objectVersionProp}}, found {$version})"); // Modify object, using object version property $json->{$objectVersionProp} = $version; $response = API::userPost(self::$config['userID'], "{$objectTypePlural}?key=" . self::$config['apiKey'], json_encode(array($objectTypePlural => array($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}?key=" . self::$config['apiKey']); $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}?key=" . self::$config['apiKey']); $version = $response->getHeader("Last-Modified-Version"); $this->assertTrue(is_numeric($version)); $this->assertEquals($version, $version3); // TODO: Version should be incremented on deleted item }
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?key=" . self::$config['apiKey'] . "&content=json&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 $response = API::userGet(self::$config['userID'], "tags?key=" . self::$config['apiKey'] . "&content=json&newer={$version}"); $this->assert200($response); $this->assertNumResults(1, $response); $this->assertGreaterThan($version, $response->getHeader('Last-Modified-Version')); $content = API::getContentFromResponse($response); $json = json_decode($content, true); $this->assertEquals("c", $json['tag']); $this->assertEquals(0, $json['type']); }
public function testContentBibSingle() { foreach (self::$styles as $style) { foreach (self::$items as $key => $expected) { $response = API::userGet(self::$config['userID'], "items/{$key}?key=" . self::$config['apiKey'] . "&content=bib" . ($style == "default" ? "" : "&style={$style}")); $this->assert200($response); $content = API::getContentFromResponse($response); // Add zapi namespace $content = str_replace('<content ', '<content xmlns:zapi="http://zotero.org/ns/api" ', $content); $this->assertXmlStringEqualsXmlString($expected['bib'][$style], $content); } } }