aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2020-12-29 12:50:23 +0100
committerArthurHoaro <arthur@hoa.ro>2020-12-29 13:01:04 +0100
commit0640c1a6db6d9a13e5d0079f0bf42497010edbc7 (patch)
tree17002fc6a277d21f728f103434bd8a70561affa5
parentfe58bdcd9e0ddca8a2a99142dc9eaee8845efa67 (diff)
downloadShaarli-0640c1a6db6d9a13e5d0079f0bf42497010edbc7.tar.gz
Shaarli-0640c1a6db6d9a13e5d0079f0bf42497010edbc7.tar.zst
Shaarli-0640c1a6db6d9a13e5d0079f0bf42497010edbc7.zip
API: POST/PUT Link - properly parse tags string
Even though the documentation specify that tags should be passed as an array, tags string is actually allowed. So this adds a proper parsing with configured separator. Related to #1651
-rw-r--r--application/api/ApiUtils.php21
-rw-r--r--application/api/controllers/Links.php12
-rw-r--r--tests/api/controllers/links/PostLinkTest.php48
-rw-r--r--tests/api/controllers/links/PutLinkTest.php48
4 files changed, 123 insertions, 6 deletions
diff --git a/application/api/ApiUtils.php b/application/api/ApiUtils.php
index 05a2840a..9228bb2d 100644
--- a/application/api/ApiUtils.php
+++ b/application/api/ApiUtils.php
@@ -91,13 +91,17 @@ class ApiUtils
91 * If no URL is provided, it will generate a local note URL. 91 * If no URL is provided, it will generate a local note URL.
92 * If no title is provided, it will use the URL as title. 92 * If no title is provided, it will use the URL as title.
93 * 93 *
94 * @param array|null $input Request Link. 94 * @param array|null $input Request Link.
95 * @param bool $defaultPrivate Setting defined if a bookmark is private by default. 95 * @param bool $defaultPrivate Setting defined if a bookmark is private by default.
96 * @param string $tagsSeparator Tags separator loaded from the config file.
96 * 97 *
97 * @return Bookmark instance. 98 * @return Bookmark instance.
98 */ 99 */
99 public static function buildBookmarkFromRequest(?array $input, bool $defaultPrivate): Bookmark 100 public static function buildBookmarkFromRequest(
100 { 101 ?array $input,
102 bool $defaultPrivate,
103 string $tagsSeparator
104 ): Bookmark {
101 $bookmark = new Bookmark(); 105 $bookmark = new Bookmark();
102 $url = ! empty($input['url']) ? cleanup_url($input['url']) : ''; 106 $url = ! empty($input['url']) ? cleanup_url($input['url']) : '';
103 if (isset($input['private'])) { 107 if (isset($input['private'])) {
@@ -109,6 +113,15 @@ class ApiUtils
109 $bookmark->setTitle(! empty($input['title']) ? $input['title'] : ''); 113 $bookmark->setTitle(! empty($input['title']) ? $input['title'] : '');
110 $bookmark->setUrl($url); 114 $bookmark->setUrl($url);
111 $bookmark->setDescription(! empty($input['description']) ? $input['description'] : ''); 115 $bookmark->setDescription(! empty($input['description']) ? $input['description'] : '');
116
117 // Be permissive with provided tags format
118 if (is_string($input['tags'] ?? null)) {
119 $input['tags'] = tags_str2array($input['tags'], $tagsSeparator);
120 }
121 if (is_array($input['tags'] ?? null) && count($input['tags']) === 1 && is_string($input['tags'][0])) {
122 $input['tags'] = tags_str2array($input['tags'][0], $tagsSeparator);
123 }
124
112 $bookmark->setTags(! empty($input['tags']) ? $input['tags'] : []); 125 $bookmark->setTags(! empty($input['tags']) ? $input['tags'] : []);
113 $bookmark->setPrivate($private); 126 $bookmark->setPrivate($private);
114 127
diff --git a/application/api/controllers/Links.php b/application/api/controllers/Links.php
index c379b962..b83b2260 100644
--- a/application/api/controllers/Links.php
+++ b/application/api/controllers/Links.php
@@ -117,7 +117,11 @@ class Links extends ApiController
117 public function postLink($request, $response) 117 public function postLink($request, $response)
118 { 118 {
119 $data = (array) ($request->getParsedBody() ?? []); 119 $data = (array) ($request->getParsedBody() ?? []);
120 $bookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links')); 120 $bookmark = ApiUtils::buildBookmarkFromRequest(
121 $data,
122 $this->conf->get('privacy.default_private_links'),
123 $this->conf->get('general.tags_separator', ' ')
124 );
121 // duplicate by URL, return 409 Conflict 125 // duplicate by URL, return 409 Conflict
122 if ( 126 if (
123 ! empty($bookmark->getUrl()) 127 ! empty($bookmark->getUrl())
@@ -158,7 +162,11 @@ class Links extends ApiController
158 $index = index_url($this->ci['environment']); 162 $index = index_url($this->ci['environment']);
159 $data = $request->getParsedBody(); 163 $data = $request->getParsedBody();
160 164
161 $requestBookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links')); 165 $requestBookmark = ApiUtils::buildBookmarkFromRequest(
166 $data,
167 $this->conf->get('privacy.default_private_links'),
168 $this->conf->get('general.tags_separator', ' ')
169 );
162 // duplicate URL on a different link, return 409 Conflict 170 // duplicate URL on a different link, return 409 Conflict
163 if ( 171 if (
164 ! empty($requestBookmark->getUrl()) 172 ! empty($requestBookmark->getUrl())
diff --git a/tests/api/controllers/links/PostLinkTest.php b/tests/api/controllers/links/PostLinkTest.php
index e12f803b..f755e2d2 100644
--- a/tests/api/controllers/links/PostLinkTest.php
+++ b/tests/api/controllers/links/PostLinkTest.php
@@ -229,4 +229,52 @@ class PostLinkTest extends TestCase
229 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) 229 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
230 ); 230 );
231 } 231 }
232
233 /**
234 * Test link creation with a tag string provided
235 */
236 public function testPostLinkWithTagString(): void
237 {
238 $link = [
239 'tags' => 'one two',
240 ];
241 $env = Environment::mock([
242 'REQUEST_METHOD' => 'POST',
243 'CONTENT_TYPE' => 'application/json'
244 ]);
245
246 $request = Request::createFromEnvironment($env);
247 $request = $request->withParsedBody($link);
248 $response = $this->controller->postLink($request, new Response());
249
250 $this->assertEquals(201, $response->getStatusCode());
251 $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]);
252 $data = json_decode((string) $response->getBody(), true);
253 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
254 $this->assertEquals(['one', 'two'], $data['tags']);
255 }
256
257 /**
258 * Test link creation with a tag string provided
259 */
260 public function testPostLinkWithTagString2(): void
261 {
262 $link = [
263 'tags' => ['one two'],
264 ];
265 $env = Environment::mock([
266 'REQUEST_METHOD' => 'POST',
267 'CONTENT_TYPE' => 'application/json'
268 ]);
269
270 $request = Request::createFromEnvironment($env);
271 $request = $request->withParsedBody($link);
272 $response = $this->controller->postLink($request, new Response());
273
274 $this->assertEquals(201, $response->getStatusCode());
275 $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]);
276 $data = json_decode((string) $response->getBody(), true);
277 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
278 $this->assertEquals(['one', 'two'], $data['tags']);
279 }
232} 280}
diff --git a/tests/api/controllers/links/PutLinkTest.php b/tests/api/controllers/links/PutLinkTest.php
index 240ee323..fe24f2eb 100644
--- a/tests/api/controllers/links/PutLinkTest.php
+++ b/tests/api/controllers/links/PutLinkTest.php
@@ -233,4 +233,52 @@ class PutLinkTest extends \Shaarli\TestCase
233 233
234 $this->controller->putLink($request, new Response(), ['id' => -1]); 234 $this->controller->putLink($request, new Response(), ['id' => -1]);
235 } 235 }
236
237 /**
238 * Test link creation with a tag string provided
239 */
240 public function testPutLinkWithTagString(): void
241 {
242 $link = [
243 'tags' => 'one two',
244 ];
245 $id = '41';
246 $env = Environment::mock([
247 'REQUEST_METHOD' => 'PUT',
248 'CONTENT_TYPE' => 'application/json'
249 ]);
250
251 $request = Request::createFromEnvironment($env);
252 $request = $request->withParsedBody($link);
253 $response = $this->controller->putLink($request, new Response(), ['id' => $id]);
254
255 $this->assertEquals(200, $response->getStatusCode());
256 $data = json_decode((string) $response->getBody(), true);
257 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
258 $this->assertEquals(['one', 'two'], $data['tags']);
259 }
260
261 /**
262 * Test link creation with a tag string provided
263 */
264 public function testPutLinkWithTagString2(): void
265 {
266 $link = [
267 'tags' => ['one two'],
268 ];
269 $id = '41';
270 $env = Environment::mock([
271 'REQUEST_METHOD' => 'PUT',
272 'CONTENT_TYPE' => 'application/json'
273 ]);
274
275 $request = Request::createFromEnvironment($env);
276 $request = $request->withParsedBody($link);
277 $response = $this->controller->putLink($request, new Response(), ['id' => $id]);
278
279 $this->assertEquals(200, $response->getStatusCode());
280 $data = json_decode((string) $response->getBody(), true);
281 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
282 $this->assertEquals(['one', 'two'], $data['tags']);
283 }
236} 284}