--- /dev/null
+.PHONY: style-check test
+
+style-check:
+ flake8 ./diaspy/
+
+test:
+ python3 -m unittest --verbose --catch --failfast tests.py
-import diaspy.client
-import diaspy.models
-import diaspy.conversations
+from diaspy import client
+from diaspy import models
+from diaspy import conversations
-import requests
import re
import json
import diaspy.models
self.connection = diaspy.connection.Connection(pod, username, password)
self.connection.login()
self.pod = pod
- self._post_data = {}
-
- def _sessionget(self, string):
- """This method gets data from session.
- Performs additional checks if needed.
-
- Example:
- To obtain 'foo' from pod one should call `_sessionget('foo')`.
-
- :param string: URL to get without the pod's URL and slash eg. 'stream'.
- :type string: str
- """
- request = self.connection.get(string)
- return request
-
- def _sessionpost(self, string, data, headers={}, params={}):
- """This method posts data to session.
- Performs additional checks if needed.
-
- Example:
- To post to 'foo' one should call `_sessionpost('foo', data={})`.
-
- :param string: URL to post without the pod's URL and slash eg. 'status_messages'.
- :type string: str
- :param data: Data to post.
- :param headers: Headers (optional).
- :type headers: dict
- :param params: Parameters (optional).
- :type params: dict
- """
- request = self.connection.post(string, data, headers, params)
- return request
-
- def _sessiondelete(self, string, data, headers={}):
- """This method lets you send delete request to session.
- Performs additional checks if needed.
-
- :param string: URL to use.
- :type string: str
- :param data: Data to use.
- :param headers: Headers to use (optional).
- :type headers: dict
- """
- request = self.connection.delete(string, data, headers)
- return request
def _setpostdata(self, text, aspect_ids, photos):
"""This function prepares data for posting.
:returns: diaspy.models.Post -- the Post which has been created
"""
r = self.connection.post('status_messages',
- data=json.dumps(self._post_data),
- headers={'content-type': 'application/json',
- 'accept': 'application/json',
- 'x-csrf-token': self.get_token()})
+ data=json.dumps(self._post_data),
+ headers={'content-type': 'application/json',
+ 'accept': 'application/json',
+ 'x-csrf-token': self.get_token()})
if r.status_code != 201:
raise Exception('{0}: Post could not be posted.'.format(
r.status_code))
raise Exception('wrong status code: {0}'.format(request.status_code))
stream = request.json()
- return [diaspy.models.Post(str(post['id']), self) for post in stream]
+ return [diaspy.models.Post(str(post['id']), self.connection) for post in stream]
def get_notifications(self):
"""This functions returns a list of notifications.
raise Exception('wrong status code: {0}'.format(r.status_code))
mentions = r.json()
- return [diaspy.models.Post(str(post['id']), self) for post in mentions]
+ return [diaspy.models.Post(str(post['id']), self.connection) for post in mentions]
def get_tag(self, tag):
"""This functions returns a list of posts containing the tag.
raise Exception('wrong status code: {0}'.format(r.status_code))
tagged_posts = r.json()
- return [diaspy.models.Post(str(post['id']), self) for post in tagged_posts]
+ return [diaspy.models.Post(str(post['id']), self.connection) for post in tagged_posts]
def get_mailbox(self):
"""This functions returns a list of messages found in the conversation.
raise Exception('wrong status code: {0}'.format(r.status_code))
mailbox = r.json()
- return [diaspy.conversations.Conversation(str(conversation['conversation']['id']), self)
+ return [diaspy.conversations.Conversation(str(conversation['conversation']['id']), self.connection)
for conversation in mailbox]
def add_user_to_aspect(self, user_id, aspect_id):
'person_id': user_id}
r = self.connection.delete('aspect_memberships/42.json',
- data=data)
+ data=data)
if r.status_code != 200:
raise Exception('wrong status code: {0}'.format(r.status_code))
data = {'authenticity_token': self.get_token()}
r = self.connection.delete('aspects/{}'.format(aspect_id),
- data=data)
+ data=data)
if r.status_code != 404:
raise Exception('wrong status code: {0}'.format(r.status_code))
'authenticity_token': self.get_token()}
r = self.connection.post('conversations/',
- data=data,
- headers={'accept': 'application/json'})
+ data=data,
+ headers={'accept': 'application/json'})
if r.status_code != 200:
raise Exception('{0}: Conversation could not be started.'
.format(r.status_code))
import requests
+class LoginError(Exception):
+ pass
+
+
class Connection():
"""Object representing connection with the server.
It is pushed around internally and is considered private.
'user[password]': self.password,
'authenticity_token': self.getToken()}
- def login(self):
- """This function is used to connect to the pod and log in.
+ def _login(self):
+ """Handles actual login request.
+ Raises LoginError if login failed.
+ """
+ request = self.post('users/sign_in',
+ data=self.login_data,
+ headers={'accept': 'application/json'})
+ if request.status_code != 201:
+ raise Exception('{0}: Login failed.'.format(request.status_code))
+
+ def login(self, username='', password=''):
+ """This function is used to log in to a pod.
+ Will raise LoginError if password or username was not specified.
"""
- r = self.post('users/sign_in',
- data=self.login_data,
- headers={'accept': 'application/json'})
- if r.status_code != 201:
- raise Exception('{0}: Login failed.'.format(r.status_code))
+ if username and password: self._setlogin(username, password)
+ if not self.username or not self.password: raise LoginError('password or username not specified')
+ self._login()
+
+ def podswitch(self, pod):
+ """Switches pod.
+ """
+ self.pod = pod
+ self._login()
def getToken(self):
- """This function gets a token needed for authentication in most cases
+ """This function returns a token needed for authentication in most cases.
:returns: string -- token used to authenticate
"""
#!/usr/bin/env python3
-class Conversation:
+class Conversation():
"""This class represents a conversation.
.. note::
Remember that you need to have access to the conversation.
-
"""
def __init__(self, conv_id, connection):
"""
'authenticity_token': self._connection.get_token()}
r = self._connection.post('conversations/{}/messages'.format(self.conv_id),
- data=data,
- headers={'accept': 'application/json'})
+ data=data,
+ headers={'accept': 'application/json'})
if r.status_code != 200:
raise Exception('{0}: Answer could not be posted.'
.format(r.status_code))
data = {'authenticity_token': self._connection.get_token()}
r = self._connection.delete('conversations/{0}/visibility/'
- .format(self.conv_id),
- data=data,
- headers={'accept': 'application/json'})
+ .format(self.conv_id),
+ data=data,
+ headers={'accept': 'application/json'})
if r.status_code != 404:
raise Exception('{0}: Conversation could not be deleted.'
data = {'authenticity_token': self._connection.getToken()}
r = self._connection.post('posts/{0}/likes'.format(self.post_id),
- data=data,
- headers={'accept': 'application/json'})
+ data=data,
+ headers={'accept': 'application/json'})
if r.status_code != 201:
raise Exception('{0}: Post could not be liked.'
post_data = self.get_data()
r = self._connection.delete('posts/{0}/likes/{1}'
- .format(self.post_id,
- post_data['interactions']
- ['likes'][0]['id']),
- data=data)
+ .format(self.post_id,
+ post_data['interactions']
+ ['likes'][0]['id']),
+ data=data)
if r.status_code != 204:
raise Exception('{0}: Like could not be removed.'
'authenticity_token': self._connection.getToken()}
r = self._connection.post('reshares',
- data=data,
- headers={'accept': 'application/json'})
+ data=data,
+ headers={'accept': 'application/json'})
if r.status_code != 201:
raise Exception('{0}: Post could not be reshared.'
'authenticity_token': self._connection.getToken()}
r = self._connection.post('posts/{0}/comments'.format(self.post_id),
- data=data,
- headers={'accept': 'application/json'})
+ data=data,
+ headers={'accept': 'application/json'})
if r.status_code != 201:
raise Exception('{0}: Comment could not be posted.'
data = {'authenticity_token': self._connection.getToken()}
r = self._connection.delete('posts/{0}/comments/{1}'
- .format(self.post_id,
- comment_id),
- data=data,
- headers={'accept': 'application/json'})
+ .format(self.post_id,
+ comment_id),
+ data=data,
+ headers={'accept': 'application/json'})
if r.status_code != 204:
raise Exception('{0}: Comment could not be deleted.'
"""
data = {'authenticity_token': self._connection.getToken()}
r = self._connection.delete('posts/{0}'.format(self.post_id),
- data=data,
- headers={'accept': 'application/json'})
+ data=data,
+ headers={'accept': 'application/json'})
if r.status_code != 204:
raise Exception('{0}: Post could not be deleted'.format(r.status_code))
--- /dev/null
+#### `Connection()` object
+
+This is the object that is used by `diaspy`'s internals.
+It is pushed around and used by various methods and other objects:
+
+* `Post()` and `Conversation()` objects require it to authenticate and
+ do their work,
+* `Client()` uses it for loggin in to pod and other stuff,
+
+
+`Connection()` is the most low-level part of `diaspy` and provides everything
+what is needed to talk to a pod.
+
+However, using only `Connection()` would be hard and cumberstone so there are
+other modules to aid you and you are strongly encouraged to use them.
+
+
+----
+
+##### Login procedure
+
+`Client()` object available in `diapsy` will login you automatically - provided
+you gave it valid pod, username and password.
+
+On the other hand, `Connection()` is more stupid and it will not log you in unless
+you explicitly order it to do so.
+Logging in with `Connection()` is done via `login()` method.
+
+**Example:**
+
+ connection = diaspy.connection.Connection(pod='https://pod.example.com')
+ connection.login('user', 'password')
+
+ OR
+
+ connection = diaspy.connection.Connection(pod='https://pod.example.com',
+ username='user',
+ password='password')
+ connection.login()
+
+
+In the example above two ways of logging in were shown.
+In the first one only *pod* is passed to the object and
+*username* and *password* were passed to `login()` method.
+
+In the second one everything is passed directly to the object being
+created and `login()` is called without any arguments.
+
+Both ways are valid and will result in exactly the same connection.
+But consider the following example:
+
+
+ connection = diaspy.connection.Connection(pod='https://pod.example.com',
+ username='user',
+ password='password')
+ connection.login(username='loser', password='passphrase')
+
+This code will result in connection with username `loser` and
+password `passphrase` because data passed to `login()` overrides data
+passed directly to object.
+
+**Remember:** if you pass something to `login()` it will not only *override* but
+also *overwrite* the username and password!
+
+
+----
+
+##### Switching pods
+
+`Connection()` provides functionality for switching pods on the fly.
+This can be achieved with `podswitch()` method.
+
+If login to a new pod is successful your connection is just changed
+overwritten but if it fails everything else also fails and the current
+connection is broken.
+
+
+----
+
+If you want to write your own interface or client for D\*
+`Connection()` will be the only object you need.
+
__passwd__ = testconf.__passwd__
+class ConnectionTest(unittest.TestCase):
+ def testLoginWithoutUsername(self):
+ connection = diaspy.connection.Connection(pod=__pod__)
+ self.assertRaises(diaspy.connection.LoginError, connection.login, password='foo')
+
+ def testLoginWithoutPassword(self):
+ connection = diaspy.connection.Connection(pod=__pod__)
+ self.assertRaises(diaspy.connection.LoginError, connection.login, username='user')
+
class ClientTests(unittest.TestCase):
def testGettingUserInfo(self):
client = diaspy.client.Client(__pod__, __username__, __passwd__)