aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2017-05-07 16:08:10 +0200
committerGitHub <noreply@github.com>2017-05-07 16:08:10 +0200
commita4af59f47103a3f9c903eeddb1e30ab9cb7344f0 (patch)
tree3585b6c24c808e75eec93978682bba1a051f7817
parentb8fcb7d4403a344158ab5d2c8979bdd002e6001d (diff)
parent61d406933e7311a3eb3c0379f1dea8b790459722 (diff)
downloadShaarli-a4af59f47103a3f9c903eeddb1e30ab9cb7344f0.tar.gz
Shaarli-a4af59f47103a3f9c903eeddb1e30ab9cb7344f0.tar.zst
Shaarli-a4af59f47103a3f9c903eeddb1e30ab9cb7344f0.zip
Merge pull request #857 from ArthurHoaro/api/history-get
API: Get History endpoint
-rw-r--r--application/api/controllers/History.php71
-rw-r--r--index.php1
-rw-r--r--tests/api/controllers/HistoryTest.php221
-rw-r--r--tests/utils/ReferenceHistory.php82
4 files changed, 375 insertions, 0 deletions
diff --git a/application/api/controllers/History.php b/application/api/controllers/History.php
new file mode 100644
index 00000000..c4ff3e5d
--- /dev/null
+++ b/application/api/controllers/History.php
@@ -0,0 +1,71 @@
1<?php
2
3
4namespace Shaarli\Api\Controllers;
5
6use Shaarli\Api\Exceptions\ApiBadParametersException;
7use Slim\Http\Request;
8use Slim\Http\Response;
9
10/**
11 * Class History
12 *
13 * REST API Controller: /history
14 *
15 * @package Shaarli\Api\Controllers
16 */
17class History extends ApiController
18{
19 /**
20 * Service providing operation regarding Shaarli datastore and settings.
21 *
22 * @param Request $request Slim request.
23 * @param Response $response Slim response.
24 *
25 * @return Response response.
26 *
27 * @throws ApiBadParametersException Invalid parameters.
28 */
29 public function getHistory($request, $response)
30 {
31 $history = (new \History($this->conf->get('resource.history')))->getHistory();
32 $history = array_reverse($history);
33
34 // Return history operations from the {offset}th, starting from {since}.
35 $since = \DateTime::createFromFormat(\DateTime::ATOM, $request->getParam('since'));
36 $offset = $request->getParam('offset');
37 if (empty($offset)) {
38 $offset = 0;
39 }
40 else if (ctype_digit($offset)) {
41 $offset = (int) $offset;
42 } else {
43 throw new ApiBadParametersException('Invalid offset');
44 }
45
46 // limit parameter is either a number of links or 'all' for everything.
47 $limit = $request->getParam('limit');
48 if (empty($limit)) {
49 $limit = count($history);
50 } else if (ctype_digit($limit)) {
51 $limit = (int) $limit;
52 } else {
53 throw new ApiBadParametersException('Invalid limit');
54 }
55
56 $out = [];
57 $i = 0;
58 foreach ($history as $entry) {
59 if ((! empty($since) && $entry['datetime'] <= $since) || count($out) >= $limit) {
60 break;
61 }
62 if (++$i > $offset) {
63 $out[$i] = $entry;
64 $out[$i]['datetime'] = $out[$i]['datetime']->format(\DateTime::ATOM);
65 }
66 }
67 $out = array_values($out);
68
69 return $response->withJson($out, 200, $this->jsonStyle);
70 }
71}
diff --git a/index.php b/index.php
index 02fe2577..cddc9eeb 100644
--- a/index.php
+++ b/index.php
@@ -2253,6 +2253,7 @@ $app->group('/api/v1', function() {
2253 $this->post('/links', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink'); 2253 $this->post('/links', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink');
2254 $this->put('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink'); 2254 $this->put('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink');
2255 $this->delete('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink'); 2255 $this->delete('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink');
2256 $this->get('/history', '\Shaarli\Api\Controllers\History:getHistory')->setName('getHistory');
2256})->add('\Shaarli\Api\ApiMiddleware'); 2257})->add('\Shaarli\Api\ApiMiddleware');
2257 2258
2258$response = $app->run(true); 2259$response = $app->run(true);
diff --git a/tests/api/controllers/HistoryTest.php b/tests/api/controllers/HistoryTest.php
new file mode 100644
index 00000000..21e9c0ba
--- /dev/null
+++ b/tests/api/controllers/HistoryTest.php
@@ -0,0 +1,221 @@
1<?php
2
3
4namespace Shaarli\Api\Controllers;
5
6
7use Shaarli\Config\ConfigManager;
8use Slim\Container;
9use Slim\Http\Environment;
10use Slim\Http\Request;
11use Slim\Http\Response;
12
13require_once 'tests/utils/ReferenceHistory.php';
14
15class HistoryTest extends \PHPUnit_Framework_TestCase
16{
17 /**
18 * @var string datastore to test write operations
19 */
20 protected static $testHistory = 'sandbox/history.php';
21
22 /**
23 * @var ConfigManager instance
24 */
25 protected $conf;
26
27 /**
28 * @var \ReferenceHistory instance.
29 */
30 protected $refHistory = null;
31
32 /**
33 * @var \History instance.
34 */
35 protected $history;
36
37 /**
38 * @var Container instance.
39 */
40 protected $container;
41
42 /**
43 * @var History controller instance.
44 */
45 protected $controller;
46
47 /**
48 * Before every test, instantiate a new Api with its config, plugins and links.
49 */
50 public function setUp()
51 {
52 $this->conf = new ConfigManager('tests/utils/config/configJson.json.php');
53 $this->refHistory = new \ReferenceHistory();
54 $this->refHistory->write(self::$testHistory);
55 $this->conf->set('resource.history', self::$testHistory);
56 $this->container = new Container();
57 $this->container['conf'] = $this->conf;
58 $this->container['db'] = true;
59
60 $this->controller = new History($this->container);
61 }
62
63 /**
64 * After every test, remove the test datastore.
65 */
66 public function tearDown()
67 {
68 @unlink(self::$testHistory);
69 }
70
71 /**
72 * Test /history service without parameter.
73 */
74 public function testGetHistory()
75 {
76 $env = Environment::mock([
77 'REQUEST_METHOD' => 'GET',
78 ]);
79 $request = Request::createFromEnvironment($env);
80
81 $response = $this->controller->getHistory($request, new Response());
82 $this->assertEquals(200, $response->getStatusCode());
83 $data = json_decode((string) $response->getBody(), true);
84
85 $this->assertEquals($this->refHistory->count(), count($data));
86
87 $this->assertEquals(\History::DELETED, $data[0]['event']);
88 $this->assertEquals(
89 \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
90 $data[0]['datetime']
91 );
92 $this->assertEquals(124, $data[0]['id']);
93
94 $this->assertEquals(\History::SETTINGS, $data[1]['event']);
95 $this->assertEquals(
96 \DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM),
97 $data[1]['datetime']
98 );
99 $this->assertNull($data[1]['id']);
100
101 $this->assertEquals(\History::UPDATED, $data[2]['event']);
102 $this->assertEquals(
103 \DateTime::createFromFormat('Ymd_His', '20170301_121214')->format(\DateTime::ATOM),
104 $data[2]['datetime']
105 );
106 $this->assertEquals(123, $data[2]['id']);
107
108 $this->assertEquals(\History::CREATED, $data[3]['event']);
109 $this->assertEquals(
110 \DateTime::createFromFormat('Ymd_His', '20170201_121214')->format(\DateTime::ATOM),
111 $data[3]['datetime']
112 );
113 $this->assertEquals(124, $data[3]['id']);
114
115 $this->assertEquals(\History::CREATED, $data[4]['event']);
116 $this->assertEquals(
117 \DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM),
118 $data[4]['datetime']
119 );
120 $this->assertEquals(123, $data[4]['id']);
121 }
122
123 /**
124 * Test /history service with limit parameter.
125 */
126 public function testGetHistoryLimit()
127 {
128 $env = Environment::mock([
129 'REQUEST_METHOD' => 'GET',
130 'QUERY_STRING' => 'limit=1'
131 ]);
132 $request = Request::createFromEnvironment($env);
133
134 $response = $this->controller->getHistory($request, new Response());
135 $this->assertEquals(200, $response->getStatusCode());
136 $data = json_decode((string) $response->getBody(), true);
137
138 $this->assertEquals(1, count($data));
139
140 $this->assertEquals(\History::DELETED, $data[0]['event']);
141 $this->assertEquals(
142 \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
143 $data[0]['datetime']
144 );
145 $this->assertEquals(124, $data[0]['id']);
146 }
147
148 /**
149 * Test /history service with offset parameter.
150 */
151 public function testGetHistoryOffset()
152 {
153 $env = Environment::mock([
154 'REQUEST_METHOD' => 'GET',
155 'QUERY_STRING' => 'offset=4'
156 ]);
157 $request = Request::createFromEnvironment($env);
158
159 $response = $this->controller->getHistory($request, new Response());
160 $this->assertEquals(200, $response->getStatusCode());
161 $data = json_decode((string) $response->getBody(), true);
162
163 $this->assertEquals(1, count($data));
164
165 $this->assertEquals(\History::CREATED, $data[0]['event']);
166 $this->assertEquals(
167 \DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM),
168 $data[0]['datetime']
169 );
170 $this->assertEquals(123, $data[0]['id']);
171 }
172
173 /**
174 * Test /history service with since parameter.
175 */
176 public function testGetHistorySince()
177 {
178 $env = Environment::mock([
179 'REQUEST_METHOD' => 'GET',
180 'QUERY_STRING' => 'since=2017-03-03T00:00:00%2B00:00'
181 ]);
182 $request = Request::createFromEnvironment($env);
183
184 $response = $this->controller->getHistory($request, new Response());
185 $this->assertEquals(200, $response->getStatusCode());
186 $data = json_decode((string) $response->getBody(), true);
187
188 $this->assertEquals(1, count($data));
189
190 $this->assertEquals(\History::DELETED, $data[0]['event']);
191 $this->assertEquals(
192 \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
193 $data[0]['datetime']
194 );
195 $this->assertEquals(124, $data[0]['id']);
196 }
197
198 /**
199 * Test /history service with since parameter.
200 */
201 public function testGetHistorySinceOffsetLimit()
202 {
203 $env = Environment::mock([
204 'REQUEST_METHOD' => 'GET',
205 'QUERY_STRING' => 'since=2017-02-01T00:00:00%2B00:00&offset=1&limit=1'
206 ]);
207 $request = Request::createFromEnvironment($env);
208
209 $response = $this->controller->getHistory($request, new Response());
210 $this->assertEquals(200, $response->getStatusCode());
211 $data = json_decode((string) $response->getBody(), true);
212
213 $this->assertEquals(1, count($data));
214
215 $this->assertEquals(\History::SETTINGS, $data[0]['event']);
216 $this->assertEquals(
217 \DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM),
218 $data[0]['datetime']
219 );
220 }
221}
diff --git a/tests/utils/ReferenceHistory.php b/tests/utils/ReferenceHistory.php
new file mode 100644
index 00000000..20284770
--- /dev/null
+++ b/tests/utils/ReferenceHistory.php
@@ -0,0 +1,82 @@
1<?php
2
3/**
4 * Populates a reference history
5 */
6class ReferenceHistory
7{
8 private $count;
9
10 private $history = [];
11
12 /**
13 * Populates the test DB with reference data
14 */
15 public function __construct()
16 {
17 $this->addEntry(
18 History::CREATED,
19 DateTime::createFromFormat('Ymd_His', '20170101_121212'),
20 123
21 );
22
23 $this->addEntry(
24 History::CREATED,
25 DateTime::createFromFormat('Ymd_His', '20170201_121214'),
26 124
27 );
28
29 $this->addEntry(
30 History::UPDATED,
31 DateTime::createFromFormat('Ymd_His', '20170301_121214'),
32 123
33 );
34
35 $this->addEntry(
36 History::SETTINGS,
37 DateTime::createFromFormat('Ymd_His', '20170302_121215')
38 );
39
40 $this->addEntry(
41 History::DELETED,
42 DateTime::createFromFormat('Ymd_His', '20170303_121216'),
43 124
44 );
45 }
46
47 /**
48 * Adds a new history entry
49 *
50 * @param string $event Event identifier
51 * @param DateTime $datetime creation date
52 * @param int $id optional: related link ID
53 */
54 protected function addEntry($event, $datetime, $id = null)
55 {
56 $link = [
57 'event' => $event,
58 'datetime' => $datetime,
59 'id' => $id,
60 ];
61 $this->history[] = $link;
62 $this->count++;
63 }
64
65 /**
66 * Writes data to the datastore
67 *
68 * @param string $filename write history content to.
69 */
70 public function write($filename)
71 {
72 FileUtils::writeFlatDB($filename, $this->history);
73 }
74
75 /**
76 * Returns the number of links in the reference data
77 */
78 public function count()
79 {
80 return $this->count;
81 }
82}