From df18314b32b05f10720936831ed43573e1792ae6 Mon Sep 17 00:00:00 2001 From: Bernhard Posselt Date: Mon, 22 Apr 2013 18:24:31 +0200 Subject: added import from google reader, fix #80 --- appinfo/database.xml | 2 +- appinfo/version | 2 +- businesslayer/feedbusinesslayer.php | 54 ++++++---- dependencyinjection/dicontainer.php | 44 ++++---- .../businesslayer/feedbusinesslayer.coffee | 8 +- js/app/services/persistence.coffee | 3 +- js/public/app.js | 16 ++- .../businesslayer/feedbusinesslayerSpec.coffee | 19 +++- js/tests/services/persistenceSpec.coffee | 3 +- tests/unit/businesslayer/FeedBusinessLayerTest.php | 102 +++++++++++++++++- tests/unit/utility/ImportParserTest.php | 114 +++++++++++++++++++++ utility/importparser.php | 70 +++++++++++++ 12 files changed, 384 insertions(+), 53 deletions(-) create mode 100644 tests/unit/utility/ImportParserTest.php create mode 100644 utility/importparser.php diff --git a/appinfo/database.xml b/appinfo/database.xml index 3198b402d..af0073098 100644 --- a/appinfo/database.xml +++ b/appinfo/database.xml @@ -112,7 +112,7 @@ true - preventUpdate + prevent_update boolean false true diff --git a/appinfo/version b/appinfo/version index 223a93930..fad030004 100644 --- a/appinfo/version +++ b/appinfo/version @@ -1 +1 @@ -8.3 \ No newline at end of file +8.4 \ No newline at end of file diff --git a/businesslayer/feedbusinesslayer.php b/businesslayer/feedbusinesslayer.php index a543f352b..aba234989 100644 --- a/businesslayer/feedbusinesslayer.php +++ b/businesslayer/feedbusinesslayer.php @@ -34,6 +34,7 @@ use \OCA\News\Db\FeedMapper; use \OCA\News\Db\ItemMapper; use \OCA\News\Utility\Fetcher; use \OCA\News\Utility\FetcherException; +use \OCA\News\Utility\ImportParser; class FeedBusinessLayer extends BusinessLayer { @@ -41,15 +42,18 @@ class FeedBusinessLayer extends BusinessLayer { private $itemMapper; private $api; private $timeFactory; + private $importParser; public function __construct(FeedMapper $feedMapper, Fetcher $feedFetcher, ItemMapper $itemMapper, API $api, - TimeFactory $timeFactory){ + TimeFactory $timeFactory, + ImportParser $importParser){ parent::__construct($feedMapper); $this->feedFetcher = $feedFetcher; $this->itemMapper = $itemMapper; $this->api = $api; $this->timeFactory = $timeFactory; + $this->importParser = $importParser; } @@ -190,28 +194,34 @@ class FeedBusinessLayer extends BusinessLayer { */ public function importGoogleReaderJSON($json, $userId) { $url = 'http://owncloud/googlereader'; + $urlHash = md5($url); + + try { + $feed = $this->mapper->findByUrlHash($urlHash, $userId); + } catch(DoesNotExistException $ex) { + $feed = new Feed(); + $feed->setUserId($userId); + $feed->setUrlHash($urlHash); + $feed->setUrl($url); + $feed->setTitle('Google Reader'); + $feed->setAdded($this->timeFactory->getTime()); + $feed->setFolderId(0); + $feed->setPreventUpdate(true); + $feed = $this->mapper->insert($feed); + } + + foreach($this->importParser->parse($json) as $item) { + $item->setFeedId($feed->getId()); + try { + $this->itemMapper->findByGuidHash( + $item->getGuidHash(), $item->getFeedId(), $userId); + } catch(DoesNotExistException $ex) { + $this->itemMapper->insert($item); + } + } + + return $this->mapper->findByUrlHash($urlHash, $userId); - // TODO: write unittests that ensure that the correct - // feed parameters are being returned - - // you need to check first if the feed exists and if it does - // use that feed to add the items and to return - // if this has not been saved, these are the values - // that need to be set fyi - $feed = new Feed(); - $feed->setUserId($userId); - $feed->setUrlHash(md5($url)); - $feed->setUrl($url); - $feed->setTitle('Google Reader'); - $feed->setAdded($this->timeFactory->getTime()); - $feed->setFolderId(0); - $feed->setPreventUpdate(true); - - - // TODO: after saving the above feed, query the feed from the - // database to get the unreadCount (this is being set in the - // sql query) see line 177 - return $feed; } diff --git a/dependencyinjection/dicontainer.php b/dependencyinjection/dicontainer.php index a4202e12c..27dae1f7b 100644 --- a/dependencyinjection/dicontainer.php +++ b/dependencyinjection/dicontainer.php @@ -25,28 +25,29 @@ namespace OCA\News\DependencyInjection; -use OCA\AppFramework\DependencyInjection\DIContainer as BaseContainer; +use \OCA\AppFramework\DependencyInjection\DIContainer as BaseContainer; -use OCA\News\Controller\PageController; -use OCA\News\Controller\FolderController; -use OCA\News\Controller\FeedController; -use OCA\News\Controller\ItemController; -use OCA\News\Controller\ExportController; -use OCA\News\Controller\UserSettingsController; +use \OCA\News\Controller\PageController; +use \OCA\News\Controller\FolderController; +use \OCA\News\Controller\FeedController; +use \OCA\News\Controller\ItemController; +use \OCA\News\Controller\ExportController; +use \OCA\News\Controller\UserSettingsController; -use OCA\News\BusinessLayer\FolderBusinessLayer; -use OCA\News\BusinessLayer\FeedBusinessLayer; -use OCA\News\BusinessLayer\ItemBusinessLayer; +use \OCA\News\BusinessLayer\FolderBusinessLayer; +use \OCA\News\BusinessLayer\FeedBusinessLayer; +use \OCA\News\BusinessLayer\ItemBusinessLayer; -use OCA\News\Db\FolderMapper; -use OCA\News\Db\FeedMapper; -use OCA\News\Db\ItemMapper; -use OCA\News\Db\StatusFlag; +use \OCA\News\Db\FolderMapper; +use \OCA\News\Db\FeedMapper; +use \OCA\News\Db\ItemMapper; +use \OCA\News\Db\StatusFlag; -use OCA\News\Utility\Fetcher; -use OCA\News\Utility\FeedFetcher; -use OCA\News\Utility\TwitterFetcher; -use OCA\News\Utility\OPMLExporter; +use \OCA\News\Utility\Fetcher; +use \OCA\News\Utility\FeedFetcher; +use \OCA\News\Utility\TwitterFetcher; +use \OCA\News\Utility\OPMLExporter; +use \OCA\News\Utility\ImportParser; class DIContainer extends BaseContainer { @@ -111,7 +112,8 @@ class DIContainer extends BaseContainer { $this['FeedBusinessLayer'] = $this->share(function($c){ return new FeedBusinessLayer($c['FeedMapper'], $c['Fetcher'], - $c['ItemMapper'], $c['API'], $c['TimeFactory']); + $c['ItemMapper'], $c['API'], $c['TimeFactory'], + $c['ImportParser']); }); $this['ItemBusinessLayer'] = $this->share(function($c){ @@ -164,6 +166,10 @@ class DIContainer extends BaseContainer { return new TwitterFetcher($c['FeedFetcher']); }); + $this['ImportParser'] = $this->share(function($c){ + return new ImportParser($c['TimeFactory']); + }); + $this['StatusFlag'] = $this->share(function($c){ return new StatusFlag(); }); diff --git a/js/app/services/businesslayer/feedbusinesslayer.coffee b/js/app/services/businesslayer/feedbusinesslayer.coffee index 93f89ad31..9a23bec43 100644 --- a/js/app/services/businesslayer/feedbusinesslayer.coffee +++ b/js/app/services/businesslayer/feedbusinesslayer.coffee @@ -188,8 +188,12 @@ FeedModel, NewLoading, _ExistsError, Utils, $rootScope, UndoQueue) -> faviconLink: 'url('+@_utils.imagePath('core', 'loading.gif')+')' @_feedModel.add(feed) - - @_persistence.importGoogleReader(json) + + onSuccess = (response) => + id = response.data.feeds[0].id + @load(id) + + @_persistence.importGoogleReader(json, onSuccess) return new FeedBusinessLayer(ShowAll, FeedModel, Persistence, ActiveFeed, diff --git a/js/app/services/persistence.coffee b/js/app/services/persistence.coffee index a5a6bc092..188f5a7c7 100644 --- a/js/app/services/persistence.coffee +++ b/js/app/services/persistence.coffee @@ -253,10 +253,11 @@ $rootScope) -> @_request.post 'news_feeds_update', params - importGoogleReader: (json) -> + importGoogleReader: (json, onSuccess) -> params = data: json: json + onSuccess: onSuccess @_request.post 'news_feeds_import_googlereader', params diff --git a/js/public/app.js b/js/public/app.js index ca7124ad7..f0c783ba1 100644 --- a/js/public/app.js +++ b/js/public/app.js @@ -961,7 +961,8 @@ License along with this library. If not, see . }; FeedBusinessLayer.prototype.importGoogleReader = function(json) { - var feed, url; + var feed, onSuccess, url, + _this = this; url = 'http://owncloud/googlereader'; if (angular.isUndefined(this._feedModel.getByUrl(url))) { @@ -974,7 +975,13 @@ License along with this library. If not, see . }; this._feedModel.add(feed); } - return this._persistence.importGoogleReader(json); + onSuccess = function(response) { + var id; + + id = response.data.feeds[0].id; + return _this.load(id); + }; + return this._persistence.importGoogleReader(json, onSuccess); }; return FeedBusinessLayer; @@ -2617,13 +2624,14 @@ License along with this library. If not, see . return this._request.post('news_feeds_update', params); }; - Persistence.prototype.importGoogleReader = function(json) { + Persistence.prototype.importGoogleReader = function(json, onSuccess) { var params; params = { data: { json: json - } + }, + onSuccess: onSuccess }; return this._request.post('news_feeds_import_googlereader', params); }; diff --git a/js/tests/services/businesslayer/feedbusinesslayerSpec.coffee b/js/tests/services/businesslayer/feedbusinesslayerSpec.coffee index 4f7e8f8b3..833af6da8 100644 --- a/js/tests/services/businesslayer/feedbusinesslayerSpec.coffee +++ b/js/tests/services/businesslayer/feedbusinesslayerSpec.coffee @@ -365,8 +365,25 @@ describe 'FeedBusinessLayer', -> it 'should create an import google reader request', => + returned = + data: + feeds: [ + {id: 3, url: 'hi'} + ] + @persistence.getItems = jasmine.createSpy('importGoogleReader') @persistence.importGoogleReader = jasmine.createSpy('importGoogleReader') + @persistence.importGoogleReader.andCallFake (data, onSuccess) => + @FeedModel.handle(returned.data.feeds) + onSuccess(returned) + json = {"test": "hi"} @FeedBusinessLayer.importGoogleReader(json) - expect(@persistence.importGoogleReader).toHaveBeenCalledWith(json) \ No newline at end of file + expect(@persistence.importGoogleReader).toHaveBeenCalledWith(json, + jasmine.any(Function)) + expect(@persistence.getItems).toHaveBeenCalledWith( + @FeedType.Feed, returned.data.feeds[0].id, 0 + ) + expect(@ActiveFeed.getId()).toBe(returned.data.feeds[0].id) + expect(@ActiveFeed.getType()).toBe(@FeedType.Feed) + diff --git a/js/tests/services/persistenceSpec.coffee b/js/tests/services/persistenceSpec.coffee index 70bcf824f..90bd82783 100644 --- a/js/tests/services/persistenceSpec.coffee +++ b/js/tests/services/persistenceSpec.coffee @@ -252,8 +252,9 @@ describe 'Persistence', -> params = data: json: {"some": "json"} + onSuccess: -> - @Persistence.importGoogleReader(params.data.json) + @Persistence.importGoogleReader(params.data.json, params.onSuccess) expect(@req.post).toHaveBeenCalledWith('news_feeds_import_googlereader', diff --git a/tests/unit/businesslayer/FeedBusinessLayerTest.php b/tests/unit/businesslayer/FeedBusinessLayerTest.php index cd044161f..0f8f61e4f 100644 --- a/tests/unit/businesslayer/FeedBusinessLayerTest.php +++ b/tests/unit/businesslayer/FeedBusinessLayerTest.php @@ -34,6 +34,7 @@ use \OCA\News\Db\Feed; use \OCA\News\Db\Item; use \OCA\News\Utility\Fetcher; use \OCA\News\Utility\FetcherException; +use \OCA\News\Utility\ImportParser; class FeedBusinessLayerTest extends \OCA\AppFramework\Utility\TestUtility { @@ -45,6 +46,7 @@ class FeedBusinessLayerTest extends \OCA\AppFramework\Utility\TestUtility { private $itemMapper; private $threshold; private $time; + private $importParser; protected function setUp(){ $this->api = $this->getAPIMock(); @@ -65,9 +67,12 @@ class FeedBusinessLayerTest extends \OCA\AppFramework\Utility\TestUtility { $this->itemMapper = $this->getMockBuilder('\OCA\News\Db\ItemMapper') ->disableOriginalConstructor() ->getMock(); + $this->importParser = $this->getMockBuilder('\OCA\News\Utility\ImportParser') + ->disableOriginalConstructor() + ->getMock(); $this->businessLayer = new FeedBusinessLayer($this->mapper, $this->fetcher, $this->itemMapper, $this->api, - $timeFactory); + $timeFactory, $this->importParser); $this->user = 'jack'; $response = 'hi'; } @@ -576,5 +581,100 @@ class FeedBusinessLayerTest extends \OCA\AppFramework\Utility\TestUtility { } + public function testImportGoogleReaderJSON(){ + $url = 'http://owncloud/googlereader'; + $urlHash = md5($url); + + $feed = new Feed(); + $feed->setId(3); + $feed->setUserId($this->user); + $feed->setUrlHash($urlHash); + $feed->setUrl($url); + $feed->setTitle('Google Reader'); + $feed->setAdded($this->time); + $feed->setFolderId(0); + $feed->setPreventUpdate(true); + + $items = array(new Item()); + + $this->mapper->expects($this->at(0)) + ->method('findByUrlHash') + ->with($this->equalTo($urlHash), + $this->equalTo($this->user)) + ->will($this->throwException(new DoesNotExistException('hi'))); + $this->mapper->expects($this->at(1)) + ->method('insert') + ->will($this->returnValue($feed)); + $this->mapper->expects($this->at(2)) + ->method('findByUrlHash') + ->with($this->equalTo($urlHash), + $this->equalTo($this->user)) + ->will($this->returnValue($feed)); + $this->importParser->expects($this->once()) + ->method('parse') + ->will($this->returnValue($items)); + $this->itemMapper->expects($this->once()) + ->method('findByGuidHash'); + $this->itemMapper->expects($this->never()) + ->method('insert'); + + + $result = $this->businessLayer->importGoogleReaderJSON(array(), $this->user); + + $this->assertEquals($feed, $result); + } + + + public function testImportGoogleReaderJSONFeedExists(){ + $url = 'http://owncloud/googlereader'; + $urlHash = md5($url); + + $feed = new Feed(); + $feed->setUserId($this->user); + $feed->setUrlHash($urlHash); + $feed->setUrl($url); + $feed->setTitle('Google Reader'); + $feed->setAdded($this->time); + $feed->setFolderId(0); + $feed->setPreventUpdate(true); + $feed->setId(3); + + $item = new Item(); + $item->setGuidHash('hi'); + $items = array($item); + $savedItem = new Item(); + $savedItem->setFeedId($feed->getId()); + $savedItem->setGuidHash('hi'); + + $in = array(); + + $this->mapper->expects($this->at(0)) + ->method('findByUrlHash') + ->with($this->equalTo($urlHash), + $this->equalTo($this->user)) + ->will($this->returnValue($feed)); + $this->mapper->expects($this->at(1)) + ->method('findByUrlHash') + ->with($this->equalTo($urlHash), + $this->equalTo($this->user)) + ->will($this->returnValue($feed)); + $this->importParser->expects($this->once()) + ->method('parse') + ->with($this->equalTo($in)) + ->will($this->returnValue($items)); + $this->itemMapper->expects($this->once()) + ->method('findByGuidHash') + ->with($this->equalTo($savedItem->getGuidHash()), + $this->equalTo($savedItem->getFeedId()), + $this->equalTo($this->user)) + ->will($this->throwException(new DoesNotExistException('ho'))); + $this->itemMapper->expects($this->once()) + ->method('insert') + ->with($this->equalTo($savedItem)); + + $result = $this->businessLayer->importGoogleReaderJSON($in, $this->user); + + $this->assertEquals($feed, $result); + } } diff --git a/tests/unit/utility/ImportParserTest.php b/tests/unit/utility/ImportParserTest.php new file mode 100644 index 000000000..5c4a6292c --- /dev/null +++ b/tests/unit/utility/ImportParserTest.php @@ -0,0 +1,114 @@ +. +* +*/ + +namespace OCA\News\Utility; + +use \OCA\News\Db\Item; + +require_once(__DIR__ . "/../../classloader.php"); + + +class ImportParserTest extends \OCA\AppFramework\Utility\TestUtility { + + private $parser; + private $time; + private $in; + + protected function setUp(){ + $this->time = 222; + $timeFactory = $this->getMockBuilder( + '\OCA\AppFramework\Utility\TimeFactory') + ->disableOriginalConstructor() + ->getMock(); + $timeFactory->expects($this->any()) + ->method('getTime') + ->will($this->returnValue($this->time)); + + $this->parser = new ImportParser($timeFactory); + $this->in = array( + 'items' => array( + array( + 'id' => "tag:google.com,2005:reader/item/f9fd1dd3c19262e1", + 'title' => "[HorribleSubs] Mushibugyo - 01 [720p].mkv", + "published" => 1365415485, + + "alternate" => array( array( + "href" => "http://www.nyaa.eu/?page=view&tid=421561", + "type" => "text/html" + )), + "summary" => array( + "content" => "1596 seeder(s), 775 leecher(s), 8005 download(s) - 316.7 MiB - Trusted" + ) + ) + ) + ); + } + + + public function testImportParserReturnsEmptyArrayIfNoInput(){ + $result = $this->parser->parse(array()); + + $this->assertEquals($result, array()); + } + + + public function testParsesItems() { + $result = $this->parser->parse($this->in); + + $out = new Item(); + $out->setTitle($this->in['items'][0]['title']); + $out->setPubDate($this->in['items'][0]['published']); + $out->setBody($this->in['items'][0]['summary']['content']); + $out->setUrl($this->in['items'][0]['alternate'][0]['href']); + $out->setGuid($this->in['items'][0]['id']); + $out->setGuidHash(md5($this->in['items'][0]['id'])); + $out->setStatus(0); + $out->setUnread(); + $out->setStarred(); + + $this->assertEquals(array($out), $result); + } + + + public function testParsesItemsNoSummary() { + $this->in['items'][0]['content']['content'] = 'hi'; + unset($this->in['items'][0]['summary']); + $result = $this->parser->parse($this->in); + + $out = new Item(); + $out->setTitle($this->in['items'][0]['title']); + $out->setPubDate($this->in['items'][0]['published']); + $out->setBody($this->in['items'][0]['content']['content']); + $out->setUrl($this->in['items'][0]['alternate'][0]['href']); + $out->setGuid($this->in['items'][0]['id']); + $out->setGuidHash(md5($this->in['items'][0]['id'])); + $out->setStatus(0); + $out->setUnread(); + $out->setStarred(); + + $this->assertEquals(array($out), $result); + } + +} diff --git a/utility/importparser.php b/utility/importparser.php new file mode 100644 index 000000000..acf52381c --- /dev/null +++ b/utility/importparser.php @@ -0,0 +1,70 @@ +. +* +*/ + +namespace OCA\News\Utility; + +use \OCA\AppFramework\Utility\TimeFactory; + +use \OCA\News\Db\Item; + + +class ImportParser { + + private $timeFactory; + + public function __construct(TimeFactory $timeFactory) { + $this->timeFactor = $timeFactory; + } + + public function parse($json){ + $items = array(); + + if(array_key_exists('items', $json)) { + foreach($json['items'] as $entry) { + $item = new Item(); + $id = $entry['id']; + $item->setGuid($id); + $item->setGuidHash(md5($id)); + $item->setTitle($entry['title']); + $item->setPubDate($entry['published']); + if(array_key_exists('summary', $entry)) { + $item->setBody($entry['summary']['content']); + } elseif(array_key_exists('content', $entry)) { + $item->setBody($entry['content']['content']); + } + + $item->setUrl($entry['alternate'][0]['href']); + $item->setStatus(0); + $item->setStarred(); + $item->setUnread(); + + array_push($items, $item); + } + } + + return $items; + } + +} -- cgit v1.2.3