aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNicolas LÅ“uillet <nicolas@loeuillet.org>2017-05-05 13:56:34 +0200
committerGitHub <noreply@github.com>2017-05-05 13:56:34 +0200
commit69803049688179e1b03ef424dec91f1b9a4f9e91 (patch)
tree3171881d9aaba386505ff9b3e3a26c59dcee8f2b
parentcebed9c01f20d47cb60259f0c002ea57a80da4d0 (diff)
parent72db15ca5d7950a604f359056fc6a627f25e4ee4 (diff)
downloadwallabag-69803049688179e1b03ef424dec91f1b9a4f9e91.tar.gz
wallabag-69803049688179e1b03ef424dec91f1b9a4f9e91.tar.zst
wallabag-69803049688179e1b03ef424dec91f1b9a4f9e91.zip
Merge pull request #3053 from wallabag/api-bulk-add
Added API endpoint to handle a list of URL
-rw-r--r--app/config/config.yml1
-rw-r--r--src/Wallabag/ApiBundle/Controller/EntryRestController.php251
-rw-r--r--src/Wallabag/CoreBundle/DependencyInjection/Configuration.php3
-rw-r--r--src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php1
-rw-r--r--tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php63
5 files changed, 236 insertions, 83 deletions
diff --git a/app/config/config.yml b/app/config/config.yml
index 4f4fb900..451809d6 100644
--- a/app/config/config.yml
+++ b/app/config/config.yml
@@ -55,6 +55,7 @@ wallabag_core:
55 list_mode: 0 55 list_mode: 0
56 fetching_error_message: | 56 fetching_error_message: |
57 wallabag can't retrieve contents for this article. Please <a href="http://doc.wallabag.org/en/master/user/errors_during_fetching.html#how-can-i-help-to-fix-that">troubleshoot this issue</a>. 57 wallabag can't retrieve contents for this article. Please <a href="http://doc.wallabag.org/en/master/user/errors_during_fetching.html#how-can-i-help-to-fix-that">troubleshoot this issue</a>.
58 api_limit_mass_actions: 10
58 59
59wallabag_user: 60wallabag_user:
60 registration_enabled: "%fosuser_registration%" 61 registration_enabled: "%fosuser_registration%"
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
index 7590efbb..dbff6065 100644
--- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
@@ -5,6 +5,7 @@ namespace Wallabag\ApiBundle\Controller;
5use Hateoas\Configuration\Route; 5use Hateoas\Configuration\Route;
6use Hateoas\Representation\Factory\PagerfantaFactory; 6use Hateoas\Representation\Factory\PagerfantaFactory;
7use Nelmio\ApiDocBundle\Annotation\ApiDoc; 7use Nelmio\ApiDocBundle\Annotation\ApiDoc;
8use Symfony\Component\HttpKernel\Exception\HttpException;
8use Symfony\Component\HttpFoundation\Request; 9use Symfony\Component\HttpFoundation\Request;
9use Symfony\Component\HttpFoundation\JsonResponse; 10use Symfony\Component\HttpFoundation\JsonResponse;
10use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 11use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
@@ -44,9 +45,7 @@ class EntryRestController extends WallabagRestController
44 $results[$url] = $res instanceof Entry ? $res->getId() : false; 45 $results[$url] = $res instanceof Entry ? $res->getId() : false;
45 } 46 }
46 47
47 $json = $this->get('serializer')->serialize($results, 'json'); 48 return $this->sendResponse($results);
48
49 return (new JsonResponse())->setJson($json);
50 } 49 }
51 50
52 // let's see if it is a simple url? 51 // let's see if it is a simple url?
@@ -62,9 +61,7 @@ class EntryRestController extends WallabagRestController
62 61
63 $exists = $res instanceof Entry ? $res->getId() : false; 62 $exists = $res instanceof Entry ? $res->getId() : false;
64 63
65 $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); 64 return $this->sendResponse(['exists' => $exists]);
66
67 return (new JsonResponse())->setJson($json);
68 } 65 }
69 66
70 /** 67 /**
@@ -124,9 +121,7 @@ class EntryRestController extends WallabagRestController
124 ) 121 )
125 ); 122 );
126 123
127 $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); 124 return $this->sendResponse($paginatedCollection);
128
129 return (new JsonResponse())->setJson($json);
130 } 125 }
131 126
132 /** 127 /**
@@ -145,9 +140,7 @@ class EntryRestController extends WallabagRestController
145 $this->validateAuthentication(); 140 $this->validateAuthentication();
146 $this->validateUserAccess($entry->getUser()->getId()); 141 $this->validateUserAccess($entry->getUser()->getId());
147 142
148 $json = $this->get('serializer')->serialize($entry, 'json'); 143 return $this->sendResponse($entry);
149
150 return (new JsonResponse())->setJson($json);
151 } 144 }
152 145
153 /** 146 /**
@@ -173,6 +166,110 @@ class EntryRestController extends WallabagRestController
173 } 166 }
174 167
175 /** 168 /**
169 * Handles an entries list and delete URL.
170 *
171 * @ApiDoc(
172 * parameters={
173 * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to delete."}
174 * }
175 * )
176 *
177 * @return JsonResponse
178 */
179 public function deleteEntriesListAction(Request $request)
180 {
181 $this->validateAuthentication();
182
183 $urls = json_decode($request->query->get('urls', []));
184
185 if (empty($urls)) {
186 return $this->sendResponse([]);
187 }
188
189 $results = [];
190
191 // handle multiple urls
192 foreach ($urls as $key => $url) {
193 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId(
194 $url,
195 $this->getUser()->getId()
196 );
197
198 $results[$key]['url'] = $url;
199
200 if (false !== $entry) {
201 $em = $this->getDoctrine()->getManager();
202 $em->remove($entry);
203 $em->flush();
204
205 // entry deleted, dispatch event about it!
206 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
207 }
208
209 $results[$key]['entry'] = $entry instanceof Entry ? true : false;
210 }
211
212 return $this->sendResponse($results);
213 }
214
215 /**
216 * Handles an entries list and create URL.
217 *
218 * @ApiDoc(
219 * parameters={
220 * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to create."}
221 * }
222 * )
223 *
224 * @return JsonResponse
225 *
226 * @throws HttpException When limit is reached
227 */
228 public function postEntriesListAction(Request $request)
229 {
230 $this->validateAuthentication();
231
232 $urls = json_decode($request->query->get('urls', []));
233 $results = [];
234
235 $limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions');
236
237 if (count($urls) > $limit) {
238 throw new HttpException(400, 'API limit reached');
239 }
240
241 // handle multiple urls
242 if (!empty($urls)) {
243 foreach ($urls as $key => $url) {
244 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId(
245 $url,
246 $this->getUser()->getId()
247 );
248
249 $results[$key]['url'] = $url;
250
251 if (false === $entry) {
252 $entry = $this->get('wallabag_core.content_proxy')->updateEntry(
253 new Entry($this->getUser()),
254 $url
255 );
256 }
257
258 $em = $this->getDoctrine()->getManager();
259 $em->persist($entry);
260 $em->flush();
261
262 $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false;
263
264 // entry saved, dispatch event about it!
265 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
266 }
267 }
268
269 return $this->sendResponse($results);
270 }
271
272 /**
176 * Create an entry. 273 * Create an entry.
177 * 274 *
178 * @ApiDoc( 275 * @ApiDoc(
@@ -229,9 +326,7 @@ class EntryRestController extends WallabagRestController
229 // entry saved, dispatch event about it! 326 // entry saved, dispatch event about it!
230 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); 327 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
231 328
232 $json = $this->get('serializer')->serialize($entry, 'json'); 329 return $this->sendResponse($entry);
233
234 return (new JsonResponse())->setJson($json);
235 } 330 }
236 331
237 /** 332 /**
@@ -280,9 +375,7 @@ class EntryRestController extends WallabagRestController
280 $em = $this->getDoctrine()->getManager(); 375 $em = $this->getDoctrine()->getManager();
281 $em->flush(); 376 $em->flush();
282 377
283 $json = $this->get('serializer')->serialize($entry, 'json'); 378 return $this->sendResponse($entry);
284
285 return (new JsonResponse())->setJson($json);
286 } 379 }
287 380
288 /** 381 /**
@@ -325,9 +418,7 @@ class EntryRestController extends WallabagRestController
325 // entry saved, dispatch event about it! 418 // entry saved, dispatch event about it!
326 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); 419 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
327 420
328 $json = $this->get('serializer')->serialize($entry, 'json'); 421 return $this->sendResponse($entry);
329
330 return (new JsonResponse())->setJson($json);
331 } 422 }
332 423
333 /** 424 /**
@@ -353,9 +444,7 @@ class EntryRestController extends WallabagRestController
353 // entry deleted, dispatch event about it! 444 // entry deleted, dispatch event about it!
354 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); 445 $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
355 446
356 $json = $this->get('serializer')->serialize($entry, 'json'); 447 return $this->sendResponse($entry);
357
358 return (new JsonResponse())->setJson($json);
359 } 448 }
360 449
361 /** 450 /**
@@ -374,9 +463,7 @@ class EntryRestController extends WallabagRestController
374 $this->validateAuthentication(); 463 $this->validateAuthentication();
375 $this->validateUserAccess($entry->getUser()->getId()); 464 $this->validateUserAccess($entry->getUser()->getId());
376 465
377 $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); 466 return $this->sendResponse($entry->getTags());
378
379 return (new JsonResponse())->setJson($json);
380 } 467 }
381 468
382 /** 469 /**
@@ -407,9 +494,7 @@ class EntryRestController extends WallabagRestController
407 $em->persist($entry); 494 $em->persist($entry);
408 $em->flush(); 495 $em->flush();
409 496
410 $json = $this->get('serializer')->serialize($entry, 'json'); 497 return $this->sendResponse($entry);
411
412 return (new JsonResponse())->setJson($json);
413 } 498 }
414 499
415 /** 500 /**
@@ -434,9 +519,7 @@ class EntryRestController extends WallabagRestController
434 $em->persist($entry); 519 $em->persist($entry);
435 $em->flush(); 520 $em->flush();
436 521
437 $json = $this->get('serializer')->serialize($entry, 'json'); 522 return $this->sendResponse($entry);
438
439 return (new JsonResponse())->setJson($json);
440 } 523 }
441 524
442 /** 525 /**
@@ -455,45 +538,46 @@ class EntryRestController extends WallabagRestController
455 $this->validateAuthentication(); 538 $this->validateAuthentication();
456 539
457 $list = json_decode($request->query->get('list', [])); 540 $list = json_decode($request->query->get('list', []));
458 $results = []; 541
542 if (empty($list)) {
543 return $this->sendResponse([]);
544 }
459 545
460 // handle multiple urls 546 // handle multiple urls
461 if (!empty($list)) { 547 $results = [];
462 foreach ($list as $key => $element) {
463 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId(
464 $element->url,
465 $this->getUser()->getId()
466 );
467 548
468 $results[$key]['url'] = $element->url; 549 foreach ($list as $key => $element) {
469 $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; 550 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId(
551 $element->url,
552 $this->getUser()->getId()
553 );
470 554
471 $tags = $element->tags; 555 $results[$key]['url'] = $element->url;
556 $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false;
472 557
473 if (false !== $entry && !(empty($tags))) { 558 $tags = $element->tags;
474 $tags = explode(',', $tags);
475 foreach ($tags as $label) {
476 $label = trim($label);
477 559
478 $tag = $this->getDoctrine() 560 if (false !== $entry && !(empty($tags))) {
479 ->getRepository('WallabagCoreBundle:Tag') 561 $tags = explode(',', $tags);
480 ->findOneByLabel($label); 562 foreach ($tags as $label) {
563 $label = trim($label);
481 564
482 if (false !== $tag) { 565 $tag = $this->getDoctrine()
483 $entry->removeTag($tag); 566 ->getRepository('WallabagCoreBundle:Tag')
484 } 567 ->findOneByLabel($label);
485 }
486 568
487 $em = $this->getDoctrine()->getManager(); 569 if (false !== $tag) {
488 $em->persist($entry); 570 $entry->removeTag($tag);
489 $em->flush(); 571 }
490 } 572 }
573
574 $em = $this->getDoctrine()->getManager();
575 $em->persist($entry);
576 $em->flush();
491 } 577 }
492 } 578 }
493 579
494 $json = $this->get('serializer')->serialize($results, 'json'); 580 return $this->sendResponse($results);
495
496 return (new JsonResponse())->setJson($json);
497 } 581 }
498 582
499 /** 583 /**
@@ -512,32 +596,47 @@ class EntryRestController extends WallabagRestController
512 $this->validateAuthentication(); 596 $this->validateAuthentication();
513 597
514 $list = json_decode($request->query->get('list', [])); 598 $list = json_decode($request->query->get('list', []));
599
600 if (empty($list)) {
601 return $this->sendResponse([]);
602 }
603
515 $results = []; 604 $results = [];
516 605
517 // handle multiple urls 606 // handle multiple urls
518 if (!empty($list)) { 607 foreach ($list as $key => $element) {
519 foreach ($list as $key => $element) { 608 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId(
520 $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( 609 $element->url,
521 $element->url, 610 $this->getUser()->getId()
522 $this->getUser()->getId() 611 );
523 );
524 612
525 $results[$key]['url'] = $element->url; 613 $results[$key]['url'] = $element->url;
526 $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; 614 $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false;
527 615
528 $tags = $element->tags; 616 $tags = $element->tags;
529 617
530 if (false !== $entry && !(empty($tags))) { 618 if (false !== $entry && !(empty($tags))) {
531 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); 619 $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
532 620
533 $em = $this->getDoctrine()->getManager(); 621 $em = $this->getDoctrine()->getManager();
534 $em->persist($entry); 622 $em->persist($entry);
535 $em->flush(); 623 $em->flush();
536 }
537 } 624 }
538 } 625 }
539 626
540 $json = $this->get('serializer')->serialize($results, 'json'); 627 return $this->sendResponse($results);
628 }
629
630 /**
631 * Shortcut to send data serialized in json.
632 *
633 * @param mixed $data
634 *
635 * @return JsonResponse
636 */
637 private function sendResponse($data)
638 {
639 $json = $this->get('serializer')->serialize($data, 'json');
541 640
542 return (new JsonResponse())->setJson($json); 641 return (new JsonResponse())->setJson($json);
543 } 642 }
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php
index 006a18c3..75b37729 100644
--- a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php
+++ b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php
@@ -47,6 +47,9 @@ class Configuration implements ConfigurationInterface
47 ->scalarNode('list_mode') 47 ->scalarNode('list_mode')
48 ->defaultValue(1) 48 ->defaultValue(1)
49 ->end() 49 ->end()
50 ->scalarNode('api_limit_mass_actions')
51 ->defaultValue(10)
52 ->end()
50 ->end() 53 ->end()
51 ; 54 ;
52 55
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php
index aa9ee339..c075c19f 100644
--- a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php
+++ b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php
@@ -26,6 +26,7 @@ class WallabagCoreExtension extends Extension
26 $container->setParameter('wallabag_core.action_mark_as_read', $config['action_mark_as_read']); 26 $container->setParameter('wallabag_core.action_mark_as_read', $config['action_mark_as_read']);
27 $container->setParameter('wallabag_core.list_mode', $config['list_mode']); 27 $container->setParameter('wallabag_core.list_mode', $config['list_mode']);
28 $container->setParameter('wallabag_core.fetching_error_message', $config['fetching_error_message']); 28 $container->setParameter('wallabag_core.fetching_error_message', $config['fetching_error_message']);
29 $container->setParameter('wallabag_core.api_limit_mass_actions', $config['api_limit_mass_actions']);
29 30
30 $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 31 $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
31 $loader->load('services.yml'); 32 $loader->load('services.yml');
diff --git a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
index e6ffd664..f0173cef 100644
--- a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
+++ b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
@@ -766,20 +766,69 @@ class EntryRestControllerTest extends WallabagApiTestCase
766 ], 766 ],
767 ]; 767 ];
768 768
769 $this->client->request('DELETE', '/api/entries/tags/list?list='.json_encode($list)); 769 $this->client->request('DELETE', '/api/entries/tags/list?list=' . json_encode($list));
770 }
771
772
773 public function testPostEntriesListAction()
774 {
775 $list = [
776 'http://www.lemonde.fr/musiques/article/2017/04/23/loin-de-la-politique-le-printemps-de-bourges-retombe-en-enfance_5115862_1654986.html',
777 'http://0.0.0.0/entry2',
778 ];
779
780 $this->client->request('POST', '/api/entries/lists?urls='.json_encode($list));
770 781
771 $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); 782 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
772 783
773 $content = json_decode($this->client->getResponse()->getContent(), true); 784 $content = json_decode($this->client->getResponse()->getContent(), true);
774 785
775 $this->assertInternalType('int', $content[0]['entry']); 786 $this->assertInternalType('int', $content[0]['entry']);
776 $this->assertEquals('http://0.0.0.0/entry4', $content[0]['url']); 787 $this->assertEquals('http://www.lemonde.fr/musiques/article/2017/04/23/loin-de-la-politique-le-printemps-de-bourges-retombe-en-enfance_5115862_1654986.html', $content[0]['url']);
777 788
778 $entry = $this->client->getContainer()->get('doctrine.orm.entity_manager') 789 $this->assertInternalType('int', $content[1]['entry']);
779 ->getRepository('WallabagCoreBundle:Entry') 790 $this->assertEquals('http://0.0.0.0/entry2', $content[1]['url']);
780 ->findByUrlAndUserId('http://0.0.0.0/entry4', 1); 791 }
781 792
782 $tags = $entry->getTags(); 793 public function testDeleteEntriesListAction()
783 $this->assertCount(2, $tags); 794 {
795 $list = [
796 'http://www.lemonde.fr/musiques/article/2017/04/23/loin-de-la-politique-le-printemps-de-bourges-retombe-en-enfance_5115862_1654986.html',
797 'http://0.0.0.0/entry3',
798 ];
799
800 $this->client->request('DELETE', '/api/entries/list?urls='.json_encode($list));
801
802 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
803
804 $content = json_decode($this->client->getResponse()->getContent(), true);
805
806 $this->assertTrue($content[0]['entry']);
807 $this->assertEquals('http://www.lemonde.fr/musiques/article/2017/04/23/loin-de-la-politique-le-printemps-de-bourges-retombe-en-enfance_5115862_1654986.html', $content[0]['url']);
808
809 $this->assertFalse($content[1]['entry']);
810 $this->assertEquals('http://0.0.0.0/entry3', $content[1]['url']);
811 }
812
813 public function testLimitBulkAction()
814 {
815 $list = [
816 'http://0.0.0.0/entry1',
817 'http://0.0.0.0/entry1',
818 'http://0.0.0.0/entry1',
819 'http://0.0.0.0/entry1',
820 'http://0.0.0.0/entry1',
821 'http://0.0.0.0/entry1',
822 'http://0.0.0.0/entry1',
823 'http://0.0.0.0/entry1',
824 'http://0.0.0.0/entry1',
825 'http://0.0.0.0/entry1',
826 'http://0.0.0.0/entry1',
827 ];
828
829 $this->client->request('POST', '/api/entries/lists?urls='.json_encode($list));
830
831 $this->assertEquals(400, $this->client->getResponse()->getStatusCode());
832 $this->assertContains('API limit reached', $this->client->getResponse()->getContent());
784 } 833 }
785} 834}