From 0640c1a6db6d9a13e5d0079f0bf42497010edbc7 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 29 Dec 2020 12:50:23 +0100 Subject: 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 --- application/api/ApiUtils.php | 21 +++++++++--- application/api/controllers/Links.php | 12 +++++-- tests/api/controllers/links/PostLinkTest.php | 48 ++++++++++++++++++++++++++++ tests/api/controllers/links/PutLinkTest.php | 48 ++++++++++++++++++++++++++++ 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 * If no URL is provided, it will generate a local note URL. * If no title is provided, it will use the URL as title. * - * @param array|null $input Request Link. - * @param bool $defaultPrivate Setting defined if a bookmark is private by default. + * @param array|null $input Request Link. + * @param bool $defaultPrivate Setting defined if a bookmark is private by default. + * @param string $tagsSeparator Tags separator loaded from the config file. * * @return Bookmark instance. */ - public static function buildBookmarkFromRequest(?array $input, bool $defaultPrivate): Bookmark - { + public static function buildBookmarkFromRequest( + ?array $input, + bool $defaultPrivate, + string $tagsSeparator + ): Bookmark { $bookmark = new Bookmark(); $url = ! empty($input['url']) ? cleanup_url($input['url']) : ''; if (isset($input['private'])) { @@ -109,6 +113,15 @@ class ApiUtils $bookmark->setTitle(! empty($input['title']) ? $input['title'] : ''); $bookmark->setUrl($url); $bookmark->setDescription(! empty($input['description']) ? $input['description'] : ''); + + // Be permissive with provided tags format + if (is_string($input['tags'] ?? null)) { + $input['tags'] = tags_str2array($input['tags'], $tagsSeparator); + } + if (is_array($input['tags'] ?? null) && count($input['tags']) === 1 && is_string($input['tags'][0])) { + $input['tags'] = tags_str2array($input['tags'][0], $tagsSeparator); + } + $bookmark->setTags(! empty($input['tags']) ? $input['tags'] : []); $bookmark->setPrivate($private); 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 public function postLink($request, $response) { $data = (array) ($request->getParsedBody() ?? []); - $bookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links')); + $bookmark = ApiUtils::buildBookmarkFromRequest( + $data, + $this->conf->get('privacy.default_private_links'), + $this->conf->get('general.tags_separator', ' ') + ); // duplicate by URL, return 409 Conflict if ( ! empty($bookmark->getUrl()) @@ -158,7 +162,11 @@ class Links extends ApiController $index = index_url($this->ci['environment']); $data = $request->getParsedBody(); - $requestBookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links')); + $requestBookmark = ApiUtils::buildBookmarkFromRequest( + $data, + $this->conf->get('privacy.default_private_links'), + $this->conf->get('general.tags_separator', ' ') + ); // duplicate URL on a different link, return 409 Conflict if ( ! 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 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) ); } + + /** + * Test link creation with a tag string provided + */ + public function testPostLinkWithTagString(): void + { + $link = [ + 'tags' => 'one two', + ]; + $env = Environment::mock([ + 'REQUEST_METHOD' => 'POST', + 'CONTENT_TYPE' => 'application/json' + ]); + + $request = Request::createFromEnvironment($env); + $request = $request->withParsedBody($link); + $response = $this->controller->postLink($request, new Response()); + + $this->assertEquals(201, $response->getStatusCode()); + $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]); + $data = json_decode((string) $response->getBody(), true); + $this->assertEquals(self::NB_FIELDS_LINK, count($data)); + $this->assertEquals(['one', 'two'], $data['tags']); + } + + /** + * Test link creation with a tag string provided + */ + public function testPostLinkWithTagString2(): void + { + $link = [ + 'tags' => ['one two'], + ]; + $env = Environment::mock([ + 'REQUEST_METHOD' => 'POST', + 'CONTENT_TYPE' => 'application/json' + ]); + + $request = Request::createFromEnvironment($env); + $request = $request->withParsedBody($link); + $response = $this->controller->postLink($request, new Response()); + + $this->assertEquals(201, $response->getStatusCode()); + $this->assertEquals('/api/v1/bookmarks/1', $response->getHeader('Location')[0]); + $data = json_decode((string) $response->getBody(), true); + $this->assertEquals(self::NB_FIELDS_LINK, count($data)); + $this->assertEquals(['one', 'two'], $data['tags']); + } } 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 $this->controller->putLink($request, new Response(), ['id' => -1]); } + + /** + * Test link creation with a tag string provided + */ + public function testPutLinkWithTagString(): void + { + $link = [ + 'tags' => 'one two', + ]; + $id = '41'; + $env = Environment::mock([ + 'REQUEST_METHOD' => 'PUT', + 'CONTENT_TYPE' => 'application/json' + ]); + + $request = Request::createFromEnvironment($env); + $request = $request->withParsedBody($link); + $response = $this->controller->putLink($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(['one', 'two'], $data['tags']); + } + + /** + * Test link creation with a tag string provided + */ + public function testPutLinkWithTagString2(): void + { + $link = [ + 'tags' => ['one two'], + ]; + $id = '41'; + $env = Environment::mock([ + 'REQUEST_METHOD' => 'PUT', + 'CONTENT_TYPE' => 'application/json' + ]); + + $request = Request::createFromEnvironment($env); + $request = $request->withParsedBody($link); + $response = $this->controller->putLink($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(['one', 'two'], $data['tags']); + } } -- cgit v1.2.3