Skip to content

Commit

Permalink
Merge pull request #2161 from devos50/add_rss_feed_endpoint
Browse files Browse the repository at this point in the history
Implemented endpoint to add a rss feed to your channel
  • Loading branch information
whirm committed May 4, 2016
2 parents 47c9187 + 63ba518 commit 09f046f
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 31 deletions.
52 changes: 42 additions & 10 deletions Tribler/Core/Modules/restapi/my_channel_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,24 +94,56 @@ def render_GET(self, request):

class MyChannelRssFeedsEndpoint(MyChannelBaseEndpoint):
"""
Return the RSS feeds in your channel.
Example response:
{
"rssfeeds": [{
"url": "http://rssprovider.com/feed.xml",
}, ...]
}
This class is responsible for handling requests regarding rss feeds in your channel.
"""

def render_GET(self, request):
def get_my_channel_object(self):
"""
Returns the Channel object associated with you channel that is used to manage rss feeds.
"""
my_channel_id = self.channel_db_handler.getMyChannelId()
channel_obj = self.session.lm.channel_manager.get_my_channel(my_channel_id)
if my_channel_id is None or channel_obj is None:
return channel_obj

def render_GET(self, request):
"""
Return the RSS feeds in your channel.
Example response:
{
"rssfeeds": [{
"url": "http://rssprovider.com/feed.xml",
}, ...]
}
"""
channel_obj = self.get_my_channel_object()
if channel_obj is None:
return MyChannelBaseEndpoint.return_404(request)

rss_list = channel_obj.get_rss_feed_url_list()
request.setHeader('Content-Type', 'text/json')
feeds_list = [{'url': rss_item} for rss_item in rss_list]

return json.dumps({"rssfeeds": feeds_list})

def render_PUT(self, request):
"""
Add a RSS feed to your channel.
Example request:
{
"rss_feed_url": "http://rssprovider.com/feed.xml"
}
"""
channel_obj = self.get_my_channel_object()
if channel_obj is None:
return MyChannelBaseEndpoint.return_404(request)

parameters = http.parse_qs(request.content.read(), 1)
if 'rss_feed_url' not in parameters or len(parameters['rss_feed_url']) == 0:
request.setResponseCode(http.BAD_REQUEST)
return json.dumps({"error": "rss_feed_url parameter missing"})

channel_obj.create_rss_feed(parameters['rss_feed_url'][0])
request.setHeader('Content-Type', 'text/json')
return json.dumps({"added": True})
24 changes: 21 additions & 3 deletions Tribler/Test/Core/Modules/RestApi/base_api_test.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import json
import urllib
from twisted.internet.defer import succeed
from twisted.web.client import Agent, readBody
from twisted.web.http_headers import Headers
from twisted.web.iweb import IBodyProducer
from zope.interface import implements
from Tribler.Core.Utilities.twisted_thread import reactor
from Tribler.Core.version import version_id
from Tribler.Test.test_as_server import TestAsServer


class POSTDataProducer(object):
"""
This class is used for posting data by the requests made during the tests.
"""
implements(IBodyProducer)

def __init__(self, data_dict):
self.body = urllib.urlencode(data_dict)
self.length = len(self.body)

def startProducing(self, consumer):
consumer.write(self.body)
return succeed(None)


class AbstractApiTest(TestAsServer):
"""
Tests for the Tribler HTTP API should create a subclass of this class.
Expand Down Expand Up @@ -37,11 +55,11 @@ def parse_response(self, response):
else:
return succeed(None)

def do_request(self, endpoint, expected_code=200, expected_json=None):
def do_request(self, endpoint, expected_code=200, expected_json=None, request_type='GET', post_data=''):
self.expected_response_code = expected_code
self.expected_response_json = expected_json

agent = Agent(reactor)
return agent.request('GET', 'http://localhost:%s/%s' % (self.session.get_http_api_port(), endpoint),
Headers({'User-Agent': ['Tribler ' + version_id]}), None)\
return agent.request(request_type, 'http://localhost:%s/%s' % (self.session.get_http_api_port(), endpoint),
Headers({'User-Agent': ['Tribler ' + version_id]}), POSTDataProducer(post_data))\
.addCallback(self.parse_response).addCallback(self.parse_body)
77 changes: 59 additions & 18 deletions Tribler/Test/Core/Modules/RestApi/test_my_channel_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,13 @@ def get_channel_name(self):
def get_channel_id(self):
return self.channel_id

class TestMyChannelEndpoints(AbstractApiTest):

class AbstractTestMyChannelEndpoints(AbstractApiTest):

def setUp(self, autoload_discovery=True):
super(TestMyChannelEndpoints, self).setUp(autoload_discovery)
super(AbstractTestMyChannelEndpoints, self).setUp(autoload_discovery)
self.channel_db_handler = self.session.open_dbhandler(NTFY_CHANNELCAST)

@deferred(timeout=10)
def test_my_channel_unknown_endpoint(self):
"""
Testing whether the API returns an error if an unknown endpoint is queried
"""
self.should_check_equality = False
return self.do_request('mychannel/thisendpointdoesnotexist123', expected_code=404)

def create_my_channel(self, name, description):
"""
Utility method to create your channel
Expand All @@ -42,6 +35,26 @@ def create_my_channel(self, name, description):
self.channel_db_handler.on_channel_from_dispersy('fakedispersyid', None, name, description)
return self.channel_db_handler.getMyChannelId()

def create_fake_channel(self, channel_name, channel_description):
# Use a fake ChannelCommunity object (we don't actually want to create a Dispersy community)
my_channel_id = self.create_my_channel(channel_name, channel_description)
self.session.lm.channel_manager = ChannelManager(self.session)

channel_obj = ChannelObject(self.session, ChannelCommunityMock(my_channel_id))
self.session.lm.channel_manager._channel_list.append(channel_obj)
return my_channel_id


class TestMyChannelEndpoints(AbstractTestMyChannelEndpoints):

@deferred(timeout=10)
def test_my_channel_unknown_endpoint(self):
"""
Testing whether the API returns an error if an unknown endpoint is queried
"""
self.should_check_equality = False
return self.do_request('mychannel/thisendpointdoesnotexist123', expected_code=404)

def insert_torrents_into_my_channel(self, torrent_list):
self.channel_db_handler.on_torrents_from_dispersy(torrent_list)

Expand Down Expand Up @@ -95,22 +108,50 @@ def test_rss_feeds_endpoint_no_my_channel(self):
self.session.lm.channel_manager = ChannelManager(self.session)
return self.do_request('mychannel/rssfeeds', expected_code=404)


class TestMyChannelRssEndpoints(AbstractTestMyChannelEndpoints):

@deferred(timeout=10)
def test_rss_feeds_endpoint_with_channel(self):
"""
Testing whether the API returns the right JSON data if a rss feeds from a channel are fetched
"""
expected_json = {u'rssfeeds': [{u'url': u'http://test1.com/feed.xml'}, {u'url': u'http://test2.com/feed.xml'}]}

# Use a fake ChannelCommunity object (we don't actually want to create a Dispersy community)
my_channel_id = self.create_my_channel("my channel", "this is a short description")
self.session.lm.channel_manager = ChannelManager(self.session)

channel_obj = ChannelObject(self.session, ChannelCommunityMock(my_channel_id))
self.session.lm.channel_manager._channel_list.append(channel_obj)

self.create_fake_channel("my channel", "this is a short description")
channel_obj = self.session.lm.channel_manager.get_channel("Channel name")
for rss_item in expected_json[u'rssfeeds']:
channel_obj.create_rss_feed(rss_item[u'url'])

return self.do_request('mychannel/rssfeeds', expected_code=200, expected_json=expected_json)

@deferred(timeout=10)
def test_add_rss_feed_no_my_channel(self):
"""
Testing whether the API returns a 404 if no channel has been created when adding a rss feed
"""
self.session.lm.channel_manager = ChannelManager(self.session)
return self.do_request('mychannel/rssfeeds', expected_code=404, request_type='PUT')

@deferred(timeout=10)
def test_add_rss_feed_no_parameter(self):
"""
Testing whether the API returns a 400 and error if the url parameter is not passed
"""
expected_json = {"error": "rss_feed_url parameter missing"}
self.create_fake_channel("my channel", "this is a short description")
return self.do_request('mychannel/rssfeeds', expected_code=400, expected_json=expected_json, request_type='PUT')

@deferred(timeout=10)
def test_add_rss_feed_with_channel(self):
"""
Testing whether the API returns a 200 if a channel has been created and when adding a rss feed
"""
def verify_rss_added(_):
channel_obj = self.session.lm.channel_manager.get_my_channel(my_channel_id)
self.assertEqual(channel_obj.get_rss_feed_url_list(), ["http://fakerssprovider.com/feed.rss"])

expected_json = {"added": True}
my_channel_id = self.create_fake_channel("my channel", "this is a short description")
post_data = {"rss_feed_url": "http://fakerssprovider.com/feed.rss"}
return self.do_request('mychannel/rssfeeds', expected_code=200, expected_json=expected_json,
request_type='PUT', post_data=post_data).addCallback(verify_rss_added)
19 changes: 19 additions & 0 deletions doc/Tribler REST API.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The Tribler REST API allows you to create your own applications with the channel
## Making requests
The API has been built using [Twisted Web](http://twistedmatrix.com/trac/wiki/TwistedWeb). Requests go over HTTP where GET requests should be used when data is fetched from the Tribler core and POST requests should be used if data in the core is manipulated (such as adding a torrent or removing a download). Responses of the requests are in JSON format.

Some requests require one or more parameters. These parameters are passed using the JSON format. An example of performing a request with parameters using the curl command line tool can be found below:

```
curl -X PUT -d "rss_feed_url=http://fakerssprovider.com/feed.rss" http://localhost:8085/mychannel/rssfeed
```

## Endpoints

### Channels
Expand All @@ -22,6 +28,7 @@ The API has been built using [Twisted Web](http://twistedmatrix.com/trac/wiki/Tw
| GET /mychannel/overview | Get the name, description and identifier of your channel |
| GET /mychannel/torrents | Get a list of torrents in your channel |
| GET /mychannel/rssfeeds | Get a list of rss feeds used by your channel |
| PUT /mychannel/rssfeeds | Add a rss feed to your channel |

### Settings

Expand Down Expand Up @@ -125,6 +132,18 @@ Returns a list of rss feeds in your channel. Each rss feed items contains the UR
}
```

## `PUT /mychannel/rssfeed`

Add a RSS feed to your channel.

### Example request:

```json
{
"rss_feed_url": "http://rssprovider.com/feed.xml"
}
```

## `GET /settings`

Returns a dictionary with the settings that the current Tribler session is using. Note that the response below is not the complete settings dictionary returned since that would be too large to display here.
Expand Down

0 comments on commit 09f046f

Please sign in to comment.