diff --git a/application/api/controllers/Links.php b/application/api/controllers/Links.php index 0a7968e..d4f1a09 100644 --- a/application/api/controllers/Links.php +++ b/application/api/controllers/Links.php @@ -4,6 +4,7 @@ namespace Shaarli\Api\Controllers; use Shaarli\Api\ApiUtils; use Shaarli\Api\Exceptions\ApiBadParametersException; +use Shaarli\Api\Exceptions\ApiLinkNotFoundException; use Slim\Http\Request; use Slim\Http\Response; @@ -58,8 +59,7 @@ class Links extends ApiController $limit = $request->getParam('limit'); if (empty($limit)) { $limit = self::$DEFAULT_LIMIT; - } - else if (ctype_digit($limit)) { + } else if (ctype_digit($limit)) { $limit = intval($limit); } else if ($limit === 'all') { $limit = count($links); @@ -83,4 +83,25 @@ class Links extends ApiController return $response->withJson($out, 200, $this->jsonStyle); } + + /** + * Return a single formatted link by its ID. + * + * @param Request $request Slim request. + * @param Response $response Slim response. + * @param array $args Path parameters. including the ID. + * + * @return Response containing the link array. + * + * @throws ApiLinkNotFoundException generating a 404 error. + */ + public function getLink($request, $response, $args) + { + if (! isset($this->linkDb[$args['id']])) { + throw new ApiLinkNotFoundException(); + } + $index = index_url($this->ci['environment']); + $out = ApiUtils::formatLink($this->linkDb[$args['id']], $index); + return $response->withJson($out, 200, $this->jsonStyle); + } } diff --git a/application/api/exceptions/ApiLinkNotFoundException.php b/application/api/exceptions/ApiLinkNotFoundException.php new file mode 100644 index 0000000..de7e14f --- /dev/null +++ b/application/api/exceptions/ApiLinkNotFoundException.php @@ -0,0 +1,32 @@ +message = 'Link not found'; + } + + /** + * {@inheritdoc} + */ + public function getApiResponse() + { + return $this->buildApiResponse(404); + } +} diff --git a/index.php b/index.php index d1eee09..8eb36d8 100644 --- a/index.php +++ b/index.php @@ -2232,12 +2232,13 @@ $app = new \Slim\App($container); $app->group('/api/v1', function() { $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo'); $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks'); + $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink'); })->add('\Shaarli\Api\ApiMiddleware'); $response = $app->run(true); // Hack to make Slim and Shaarli router work together: -// If a Slim route isn't found, we call renderPage(). -if ($response->getStatusCode() == 404) { +// If a Slim route isn't found and NOT API call, we call renderPage(). +if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) { // We use UTF-8 for proper international characters handling. header('Content-Type: text/html; charset=utf-8'); renderPage($conf, $pluginManager, $linkDb); diff --git a/tests/api/controllers/GetLinkIdTest.php b/tests/api/controllers/GetLinkIdTest.php new file mode 100644 index 0000000..1b02050 --- /dev/null +++ b/tests/api/controllers/GetLinkIdTest.php @@ -0,0 +1,130 @@ +conf = new \ConfigManager('tests/utils/config/configJson'); + $this->refDB = new \ReferenceLinkDB(); + $this->refDB->write(self::$testDatastore); + + $this->container = new Container(); + $this->container['conf'] = $this->conf; + $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); + + $this->controller = new Links($this->container); + } + + /** + * After each test, remove the test datastore. + */ + public function tearDown() + { + @unlink(self::$testDatastore); + } + + /** + * Test basic getLink service: return link ID=41. + */ + public function testGetLinkId() + { + // Used by index_url(). + $_SERVER['SERVER_NAME'] = 'domain.tld'; + $_SERVER['SERVER_PORT'] = 80; + $_SERVER['SCRIPT_NAME'] = '/'; + + $id = 41; + $env = Environment::mock([ + 'REQUEST_METHOD' => 'GET', + ]); + $request = Request::createFromEnvironment($env); + + $response = $this->controller->getLink($request, new Response(), ['id' => $id]); + $this->assertEquals(200, $response->getStatusCode()); + $data = json_decode((string) $response->getBody(), true); + $this->assertEquals(self::NB_FIELDS_LINK, count($data)); + $this->assertEquals($id, $data['id']); + + // Check link elements + $this->assertEquals('http://domain.tld/?WDWyig', $data['url']); + $this->assertEquals('WDWyig', $data['shorturl']); + $this->assertEquals('Link title: @website', $data['title']); + $this->assertEquals( + 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag', + $data['description'] + ); + $this->assertEquals('sTuff', $data['tags'][0]); + $this->assertEquals(false, $data['private']); + $this->assertEquals( + \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), + $data['created'] + ); + $this->assertEmpty($data['updated']); + } + + /** + * Test basic getLink service: get non existent link => ApiLinkNotFoundException. + * + * @expectedException Shaarli\Api\Exceptions\ApiLinkNotFoundException + * @expectedExceptionMessage Link not found + */ + public function testGetLink404() + { + $env = Environment::mock([ + 'REQUEST_METHOD' => 'GET', + ]); + $request = Request::createFromEnvironment($env); + + $this->controller->getLink($request, new Response(), ['id' => -1]); + } +} diff --git a/tests/api/controllers/LinksTest.php b/tests/api/controllers/GetLinksTest.php similarity index 98% rename from tests/api/controllers/LinksTest.php rename to tests/api/controllers/GetLinksTest.php index 284c3a9..da54fcf 100644 --- a/tests/api/controllers/LinksTest.php +++ b/tests/api/controllers/GetLinksTest.php @@ -9,14 +9,15 @@ use Slim\Http\Request; use Slim\Http\Response; /** - * Class LinksTest + * Class GetLinksTest * - * Test Links REST API services. - * Note that api call results are tightly related to data contained in ReferenceLinkDB. + * Test get Link list REST API service. + * + * @see http://shaarli.github.io/api-documentation/#links-links-collection-get * * @package Shaarli\Api\Controllers */ -class LinksTest extends \PHPUnit_Framework_TestCase +class GetLinksTest extends \PHPUnit_Framework_TestCase { /** * @var string datastore to test write operations @@ -53,7 +54,7 @@ class LinksTest extends \PHPUnit_Framework_TestCase */ public function setUp() { - $this->conf = new \ConfigManager('tests/utils/config/configJson.json.php'); + $this->conf = new \ConfigManager('tests/utils/config/configJson'); $this->refDB = new \ReferenceLinkDB(); $this->refDB->write(self::$testDatastore); @@ -100,7 +101,7 @@ class LinksTest extends \PHPUnit_Framework_TestCase $this->assertEquals($order[$cpt++], $link['id']); } - // Check first element fields\ + // Check first element fields $first = $data[0]; $this->assertEquals('http://domain.tld/?WDWyig', $first['url']); $this->assertEquals('WDWyig', $first['shorturl']);