Added __repr__() method to Connection()
[diaspy.git] / diaspy / connection.py
CommitLineData
a29d3526
MM
1#!/usr/bin/env python
2
8993810c
MM
3import re
4import requests
7a31b4aa 5import json
ff3d2ab4 6import warnings
8993810c 7
a29d3526 8
178faa46
MM
9"""This module abstracts connection to pod.
10"""
11
12
385e7ebe
MM
13class LoginError(Exception):
14 pass
15
16
b0b4c46d
MM
17class TokenError(Exception):
18 pass
19
20
a29d3526
MM
21class Connection():
22 """Object representing connection with the server.
23 It is pushed around internally and is considered private.
24 """
27a28aaf
MM
25 _token_regex = re.compile(r'content="(.*?)"\s+name="csrf-token')
26 _userinfo_regex = re.compile(r'window.current_user_attributes = ({.*})')
27 login_data = {}
b0b4c46d 28 token = ''
27a28aaf 29
a29d3526 30 def __init__(self, pod, username='', password=''):
8993810c
MM
31 """
32 :param pod: The complete url of the diaspora pod to use.
33 :type pod: str
34 :param username: The username used to log in.
35 :type username: str
36 :param password: The password used to log in.
37 :type password: str
38 """
a29d3526 39 self.pod = pod
8993810c 40 self.session = requests.Session()
1513c0d9
MM
41 try:
42 self._setlogin(username, password)
43 except Exception as e:
44 raise LoginError('cannot create login data (caused by: {0}'.format(e))
8993810c 45
d4bd92cd
MM
46 def __repr__(self):
47 """Returns token string.
48 It will be easier to change backend if programs will just use:
49 repr(connection)
50 instead of calling a specified method.
51 """
52 return self.get_token()
53
33b34938 54 def get(self, string, headers={}, params={}):
8993810c
MM
55 """This method gets data from session.
56 Performs additional checks if needed.
57
58 Example:
7a31b4aa 59 To obtain 'foo' from pod one should call `get('foo')`.
8993810c
MM
60
61 :param string: URL to get without the pod's URL and slash eg. 'stream'.
62 :type string: str
63 """
33b34938 64 return self.session.get('{0}/{1}'.format(self.pod, string), params=params, headers=headers)
8993810c
MM
65
66 def post(self, string, data, headers={}, params={}):
67 """This method posts data to session.
68 Performs additional checks if needed.
69
70 Example:
7a31b4aa 71 To post to 'foo' one should call `post('foo', data={})`.
8993810c
MM
72
73 :param string: URL to post without the pod's URL and slash eg. 'status_messages'.
74 :type string: str
75 :param data: Data to post.
76 :param headers: Headers (optional).
77 :type headers: dict
78 :param params: Parameters (optional).
79 :type params: dict
80 """
81 string = '{0}/{1}'.format(self.pod, string)
75fcdd7d 82 request = self.session.post(string, data, headers=headers, params=params)
8993810c
MM
83 return request
84
178faa46
MM
85 def put(self, string, data=None, headers={}, params={}):
86 """This method PUTs to session.
87 """
88 string = '{0}/{1}'.format(self.pod, string)
89 if data is not None: request = self.session.put(string, data, headers=headers, params=params)
90 else: request = self.session.put(string, headers=headers, params=params)
91 return request
92
8993810c
MM
93 def delete(self, string, data, headers={}):
94 """This method lets you send delete request to session.
95 Performs additional checks if needed.
96
97 :param string: URL to use.
98 :type string: str
99 :param data: Data to use.
100 :param headers: Headers to use (optional).
101 :type headers: dict
102 """
103 string = '{0}/{1}'.format(self.pod, string)
75fcdd7d 104 request = self.session.delete(string, data=data, headers=headers)
8993810c
MM
105 return request
106
107 def _setlogin(self, username, password):
108 """This function is used to set data for login.
b0b4c46d 109
8993810c
MM
110 .. note::
111 It should be called before _login() function.
112 """
a29d3526 113 self.username, self.password = username, password
8993810c
MM
114 self.login_data = {'user[username]': self.username,
115 'user[password]': self.password,
b0b4c46d 116 'authenticity_token': self._fetchtoken()}
8993810c 117
385e7ebe
MM
118 def _login(self):
119 """Handles actual login request.
120 Raises LoginError if login failed.
121 """
122 request = self.post('users/sign_in',
123 data=self.login_data,
124 headers={'accept': 'application/json'})
125 if request.status_code != 201:
b0b4c46d 126 raise LoginError('{0}: login failed'.format(request.status_code))
385e7ebe
MM
127
128 def login(self, username='', password=''):
129 """This function is used to log in to a pod.
130 Will raise LoginError if password or username was not specified.
8993810c 131 """
385e7ebe
MM
132 if username and password: self._setlogin(username, password)
133 if not self.username or not self.password: raise LoginError('password or username not specified')
134 self._login()
135
63cc182d
MM
136 def logout(self):
137 """Logs out from a pod.
138 When logged out you can't do anything.
139 """
140 self.get('users/sign_out')
b0b4c46d
MM
141 self.username = ''
142 self.token = ''
143 self.password = ''
63cc182d 144
385e7ebe 145 def podswitch(self, pod):
7a31b4aa 146 """Switches pod from current to another one.
385e7ebe
MM
147 """
148 self.pod = pod
149 self._login()
8993810c 150
7a31b4aa
MM
151 def getUserInfo(self):
152 """This function returns the current user's attributes.
153
154 :returns: dict -- json formatted user info.
155 """
156 request = self.get('bookmarklet')
157 userdata = json.loads(self._userinfo_regex.search(request.text).group(1))
158 return userdata
159
b0b4c46d
MM
160 def _fetchtoken(self):
161 """This method tries to get token string needed for authentication on D*.
162
163 :returns: token string
164 """
165 request = self.get('stream')
166 token = self._token_regex.search(request.text).group(1)
167 return token
2ec93347 168
ff3d2ab4 169 def get_token(self, new=False):
385e7ebe 170 """This function returns a token needed for authentication in most cases.
b0b4c46d
MM
171 Each time it is run a _fetchtoken() is called and refreshed token is stored.
172
173 It is more safe to use than _fetchtoken().
ff3d2ab4
MM
174 By setting new you can request new token or decide to get stored one.
175 If no token is stored new one will be fatched anyway.
a29d3526 176
8993810c
MM
177 :returns: string -- token used to authenticate
178 """
b0b4c46d 179 try:
ff3d2ab4
MM
180 if new: self.token = self._fetchtoken()
181 if not self.token: self.token = self._fetchtoken()
b0b4c46d 182 except requests.exceptions.ConnectionError as e:
ff3d2ab4 183 warnings.warn('{0} was cought: reusing old token'.format(e))
b0b4c46d 184 finally:
ff3d2ab4
MM
185 if not self.token: raise TokenError('cannot obtain token and no previous token found for reuse')
186 return self.token