diff options
author | Craig Roberts <craig@craig0990.co.uk> | 2018-04-09 17:24:45 +0800 |
---|---|---|
committer | Jeremy Benoist <jeremy.benoist@gmail.com> | 2019-01-22 20:42:24 +0100 |
commit | b32057980e33e7ddd93480017496a589006b8260 (patch) | |
tree | 901d98556d378b68c2e654f3c892616b302331aa | |
parent | 3527c300215e6e6010efb8bb840b8b6f5c63a1cc (diff) | |
download | wallabag-b32057980e33e7ddd93480017496a589006b8260.tar.gz wallabag-b32057980e33e7ddd93480017496a589006b8260.tar.zst wallabag-b32057980e33e7ddd93480017496a589006b8260.zip |
Fixes [wallabag/wallabag#2611] Add a basic Search REST endpoint
- Adds a new `search` key to `src/Wallabag/ApiBundle/Resources/config/routing_rest.yml`
- Reuses the `getBuilderForSearchByUser` method from the EntryRepository
- Supports, `term`, `page`, and `perPage` query parameters
- Some very basic tests
3 files changed, 169 insertions, 0 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/SearchRestController.php b/src/Wallabag/ApiBundle/Controller/SearchRestController.php new file mode 100644 index 00000000..c0b2cb24 --- /dev/null +++ b/src/Wallabag/ApiBundle/Controller/SearchRestController.php | |||
@@ -0,0 +1,91 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\ApiBundle\Controller; | ||
4 | |||
5 | use Hateoas\Configuration\Route; | ||
6 | use Hateoas\Representation\Factory\PagerfantaFactory; | ||
7 | use JMS\Serializer\SerializationContext; | ||
8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | ||
9 | use Pagerfanta\Adapter\DoctrineORMAdapter; | ||
10 | use Pagerfanta\Pagerfanta; | ||
11 | use Symfony\Component\HttpFoundation\JsonResponse; | ||
12 | use Symfony\Component\HttpFoundation\Request; | ||
13 | use Symfony\Component\HttpFoundation\Response; | ||
14 | use Symfony\Component\HttpKernel\Exception\HttpException; | ||
15 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
16 | use Wallabag\CoreBundle\Entity\Entry; | ||
17 | use Wallabag\CoreBundle\Entity\Tag; | ||
18 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; | ||
19 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
20 | |||
21 | class SearchRestController extends WallabagRestController | ||
22 | { | ||
23 | /** | ||
24 | * Search all entries by term. | ||
25 | * | ||
26 | * @ApiDoc( | ||
27 | * parameters={ | ||
28 | * {"name"="term", "dataType"="string", "required"=false, "format"="any", "description"="Any query term"}, | ||
29 | * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."}, | ||
30 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."} | ||
31 | * } | ||
32 | * ) | ||
33 | * | ||
34 | * @return JsonResponse | ||
35 | */ | ||
36 | public function getSearchAction(Request $request) | ||
37 | { | ||
38 | $this->validateAuthentication(); | ||
39 | |||
40 | $term = $request->query->get('term'); | ||
41 | $page = (int) $request->query->get('page', 1); | ||
42 | $perPage = (int) $request->query->get('perPage', 30); | ||
43 | |||
44 | $qb = $this->get('wallabag_core.entry_repository') | ||
45 | ->getBuilderForSearchByUser( | ||
46 | $this->getUser()->getId(), | ||
47 | $term, | ||
48 | null | ||
49 | ); | ||
50 | |||
51 | $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); | ||
52 | $pager = new Pagerfanta($pagerAdapter); | ||
53 | |||
54 | $pager->setMaxPerPage($perPage); | ||
55 | $pager->setCurrentPage($page); | ||
56 | |||
57 | $pagerfantaFactory = new PagerfantaFactory('page', 'perPage'); | ||
58 | $paginatedCollection = $pagerfantaFactory->createRepresentation( | ||
59 | $pager, | ||
60 | new Route( | ||
61 | 'api_get_search', | ||
62 | [ | ||
63 | 'term' => $term, | ||
64 | 'page' => $page, | ||
65 | 'perPage' => $perPage, | ||
66 | ], | ||
67 | UrlGeneratorInterface::ABSOLUTE_URL | ||
68 | ) | ||
69 | ); | ||
70 | |||
71 | return $this->sendResponse($paginatedCollection); | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * Shortcut to send data serialized in json. | ||
76 | * | ||
77 | * @param mixed $data | ||
78 | * | ||
79 | * @return JsonResponse | ||
80 | */ | ||
81 | private function sendResponse($data) | ||
82 | { | ||
83 | // https://github.com/schmittjoh/JMSSerializerBundle/issues/293 | ||
84 | $context = new SerializationContext(); | ||
85 | $context->setSerializeNull(true); | ||
86 | |||
87 | $json = $this->get('jms_serializer')->serialize($data, 'json', $context); | ||
88 | |||
89 | return (new JsonResponse())->setJson($json); | ||
90 | } | ||
91 | } | ||
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml index c0283e71..06e62c37 100644 --- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml +++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml | |||
@@ -3,6 +3,11 @@ entry: | |||
3 | resource: "WallabagApiBundle:EntryRest" | 3 | resource: "WallabagApiBundle:EntryRest" |
4 | name_prefix: api_ | 4 | name_prefix: api_ |
5 | 5 | ||
6 | search: | ||
7 | type: rest | ||
8 | resource: "WallabagApiBundle:SearchRest" | ||
9 | name_prefix: api_ | ||
10 | |||
6 | tag: | 11 | tag: |
7 | type: rest | 12 | type: rest |
8 | resource: "WallabagApiBundle:TagRest" | 13 | resource: "WallabagApiBundle:TagRest" |
diff --git a/tests/Wallabag/ApiBundle/Controller/SearchRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/SearchRestControllerTest.php new file mode 100644 index 00000000..5900ae53 --- /dev/null +++ b/tests/Wallabag/ApiBundle/Controller/SearchRestControllerTest.php | |||
@@ -0,0 +1,73 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Tests\Wallabag\ApiBundle\Controller; | ||
4 | |||
5 | use Tests\Wallabag\ApiBundle\WallabagApiTestCase; | ||
6 | use Wallabag\CoreBundle\Entity\Entry; | ||
7 | use Wallabag\CoreBundle\Entity\Tag; | ||
8 | use Wallabag\CoreBundle\Helper\ContentProxy; | ||
9 | use Wallabag\UserBundle\Entity\User; | ||
10 | |||
11 | class SearchRestControllerTest extends WallabagApiTestCase | ||
12 | { | ||
13 | public function testGetSearchWithFullOptions() | ||
14 | { | ||
15 | $this->client->request('GET', '/api/search', [ | ||
16 | 'page' => 1, | ||
17 | 'perPage' => 2, | ||
18 | 'term' => 'entry' // 6 results | ||
19 | ]); | ||
20 | |||
21 | $this->assertSame(200, $this->client->getResponse()->getStatusCode()); | ||
22 | |||
23 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
24 | |||
25 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
26 | $this->assertArrayHasKey('items', $content['_embedded']); | ||
27 | $this->assertGreaterThanOrEqual(0, $content['total']); | ||
28 | $this->assertSame(1, $content['page']); | ||
29 | $this->assertSame(2, $content['limit']); | ||
30 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
31 | |||
32 | $this->assertArrayHasKey('_links', $content); | ||
33 | $this->assertArrayHasKey('self', $content['_links']); | ||
34 | $this->assertArrayHasKey('first', $content['_links']); | ||
35 | $this->assertArrayHasKey('last', $content['_links']); | ||
36 | |||
37 | foreach (['self', 'first', 'last'] as $link) { | ||
38 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
39 | $this->assertContains('term=entry', $content['_links'][$link]['href']); | ||
40 | } | ||
41 | |||
42 | $this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
43 | } | ||
44 | |||
45 | public function testGetSearchWithNoLimit() | ||
46 | { | ||
47 | $this->client->request('GET', '/api/search', [ | ||
48 | 'term' => 'entry' | ||
49 | ]); | ||
50 | |||
51 | $this->assertSame(200, $this->client->getResponse()->getStatusCode()); | ||
52 | |||
53 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
54 | |||
55 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
56 | $this->assertArrayHasKey('items', $content['_embedded']); | ||
57 | $this->assertGreaterThanOrEqual(0, $content['total']); | ||
58 | $this->assertSame(1, $content['page']); | ||
59 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
60 | |||
61 | $this->assertArrayHasKey('_links', $content); | ||
62 | $this->assertArrayHasKey('self', $content['_links']); | ||
63 | $this->assertArrayHasKey('first', $content['_links']); | ||
64 | $this->assertArrayHasKey('last', $content['_links']); | ||
65 | |||
66 | foreach (['self', 'first', 'last'] as $link) { | ||
67 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
68 | $this->assertContains('term=entry', $content['_links'][$link]['href']); | ||
69 | } | ||
70 | |||
71 | $this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
72 | } | ||
73 | } | ||