API: Get History endpoint

See http://shaarli.github.io/api-documentation/#links-history-get
This commit is contained in:
ArthurHoaro 2017-05-06 19:39:39 +02:00
parent b8fcb7d440
commit 61d406933e
4 changed files with 375 additions and 0 deletions

View file

@ -0,0 +1,71 @@
<?php
namespace Shaarli\Api\Controllers;
use Shaarli\Api\Exceptions\ApiBadParametersException;
use Slim\Http\Request;
use Slim\Http\Response;
/**
* Class History
*
* REST API Controller: /history
*
* @package Shaarli\Api\Controllers
*/
class History extends ApiController
{
/**
* Service providing operation regarding Shaarli datastore and settings.
*
* @param Request $request Slim request.
* @param Response $response Slim response.
*
* @return Response response.
*
* @throws ApiBadParametersException Invalid parameters.
*/
public function getHistory($request, $response)
{
$history = (new \History($this->conf->get('resource.history')))->getHistory();
$history = array_reverse($history);
// Return history operations from the {offset}th, starting from {since}.
$since = \DateTime::createFromFormat(\DateTime::ATOM, $request->getParam('since'));
$offset = $request->getParam('offset');
if (empty($offset)) {
$offset = 0;
}
else if (ctype_digit($offset)) {
$offset = (int) $offset;
} else {
throw new ApiBadParametersException('Invalid offset');
}
// limit parameter is either a number of links or 'all' for everything.
$limit = $request->getParam('limit');
if (empty($limit)) {
$limit = count($history);
} else if (ctype_digit($limit)) {
$limit = (int) $limit;
} else {
throw new ApiBadParametersException('Invalid limit');
}
$out = [];
$i = 0;
foreach ($history as $entry) {
if ((! empty($since) && $entry['datetime'] <= $since) || count($out) >= $limit) {
break;
}
if (++$i > $offset) {
$out[$i] = $entry;
$out[$i]['datetime'] = $out[$i]['datetime']->format(\DateTime::ATOM);
}
}
$out = array_values($out);
return $response->withJson($out, 200, $this->jsonStyle);
}
}

View file

@ -2253,6 +2253,7 @@ $app->group('/api/v1', function() {
$this->post('/links', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink');
$this->put('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink');
$this->delete('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink');
$this->get('/history', '\Shaarli\Api\Controllers\History:getHistory')->setName('getHistory');
})->add('\Shaarli\Api\ApiMiddleware');
$response = $app->run(true);

View file

@ -0,0 +1,221 @@
<?php
namespace Shaarli\Api\Controllers;
use Shaarli\Config\ConfigManager;
use Slim\Container;
use Slim\Http\Environment;
use Slim\Http\Request;
use Slim\Http\Response;
require_once 'tests/utils/ReferenceHistory.php';
class HistoryTest extends \PHPUnit_Framework_TestCase
{
/**
* @var string datastore to test write operations
*/
protected static $testHistory = 'sandbox/history.php';
/**
* @var ConfigManager instance
*/
protected $conf;
/**
* @var \ReferenceHistory instance.
*/
protected $refHistory = null;
/**
* @var \History instance.
*/
protected $history;
/**
* @var Container instance.
*/
protected $container;
/**
* @var History controller instance.
*/
protected $controller;
/**
* Before every test, instantiate a new Api with its config, plugins and links.
*/
public function setUp()
{
$this->conf = new ConfigManager('tests/utils/config/configJson.json.php');
$this->refHistory = new \ReferenceHistory();
$this->refHistory->write(self::$testHistory);
$this->conf->set('resource.history', self::$testHistory);
$this->container = new Container();
$this->container['conf'] = $this->conf;
$this->container['db'] = true;
$this->controller = new History($this->container);
}
/**
* After every test, remove the test datastore.
*/
public function tearDown()
{
@unlink(self::$testHistory);
}
/**
* Test /history service without parameter.
*/
public function testGetHistory()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getHistory($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals($this->refHistory->count(), count($data));
$this->assertEquals(\History::DELETED, $data[0]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
$data[0]['datetime']
);
$this->assertEquals(124, $data[0]['id']);
$this->assertEquals(\History::SETTINGS, $data[1]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM),
$data[1]['datetime']
);
$this->assertNull($data[1]['id']);
$this->assertEquals(\History::UPDATED, $data[2]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170301_121214')->format(\DateTime::ATOM),
$data[2]['datetime']
);
$this->assertEquals(123, $data[2]['id']);
$this->assertEquals(\History::CREATED, $data[3]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170201_121214')->format(\DateTime::ATOM),
$data[3]['datetime']
);
$this->assertEquals(124, $data[3]['id']);
$this->assertEquals(\History::CREATED, $data[4]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM),
$data[4]['datetime']
);
$this->assertEquals(123, $data[4]['id']);
}
/**
* Test /history service with limit parameter.
*/
public function testGetHistoryLimit()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'limit=1'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getHistory($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(\History::DELETED, $data[0]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
$data[0]['datetime']
);
$this->assertEquals(124, $data[0]['id']);
}
/**
* Test /history service with offset parameter.
*/
public function testGetHistoryOffset()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'offset=4'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getHistory($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(\History::CREATED, $data[0]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM),
$data[0]['datetime']
);
$this->assertEquals(123, $data[0]['id']);
}
/**
* Test /history service with since parameter.
*/
public function testGetHistorySince()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'since=2017-03-03T00:00:00%2B00:00'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getHistory($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(\History::DELETED, $data[0]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
$data[0]['datetime']
);
$this->assertEquals(124, $data[0]['id']);
}
/**
* Test /history service with since parameter.
*/
public function testGetHistorySinceOffsetLimit()
{
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'since=2017-02-01T00:00:00%2B00:00&offset=1&limit=1'
]);
$request = Request::createFromEnvironment($env);
$response = $this->controller->getHistory($request, new Response());
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode((string) $response->getBody(), true);
$this->assertEquals(1, count($data));
$this->assertEquals(\History::SETTINGS, $data[0]['event']);
$this->assertEquals(
\DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM),
$data[0]['datetime']
);
}
}

View file

@ -0,0 +1,82 @@
<?php
/**
* Populates a reference history
*/
class ReferenceHistory
{
private $count;
private $history = [];
/**
* Populates the test DB with reference data
*/
public function __construct()
{
$this->addEntry(
History::CREATED,
DateTime::createFromFormat('Ymd_His', '20170101_121212'),
123
);
$this->addEntry(
History::CREATED,
DateTime::createFromFormat('Ymd_His', '20170201_121214'),
124
);
$this->addEntry(
History::UPDATED,
DateTime::createFromFormat('Ymd_His', '20170301_121214'),
123
);
$this->addEntry(
History::SETTINGS,
DateTime::createFromFormat('Ymd_His', '20170302_121215')
);
$this->addEntry(
History::DELETED,
DateTime::createFromFormat('Ymd_His', '20170303_121216'),
124
);
}
/**
* Adds a new history entry
*
* @param string $event Event identifier
* @param DateTime $datetime creation date
* @param int $id optional: related link ID
*/
protected function addEntry($event, $datetime, $id = null)
{
$link = [
'event' => $event,
'datetime' => $datetime,
'id' => $id,
];
$this->history[] = $link;
$this->count++;
}
/**
* Writes data to the datastore
*
* @param string $filename write history content to.
*/
public function write($filename)
{
FileUtils::writeFlatDB($filename, $this->history);
}
/**
* Returns the number of links in the reference data
*/
public function count()
{
return $this->count;
}
}