Browse Source

merge with better_api_doc

Guénaël Muller 6 years ago
parent
commit
adb6e8d958

+ 259 - 0
development.ini.old View File

@@ -0,0 +1,259 @@
1
+###
2
+# app configuration
3
+# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
4
+###
5
+[app:main]
6
+use = egg:tracim_backend
7
+
8
+pyramid.reload_templates = true
9
+pyramid.debug_authorization = false
10
+pyramid.debug_notfound = false
11
+pyramid.debug_routematch = false
12
+pyramid.default_locale_name = en
13
+pyramid.includes =
14
+    pyramid_debugtoolbar
15
+
16
+sqlalchemy.url = sqlite:///%(here)s/tracim.sqlite
17
+
18
+retry.attempts = 3
19
+
20
+# By default, the toolbar only appears for clients from IP addresses
21
+# '127.0.0.1' and '::1'.
22
+# debugtoolbar.hosts = 127.0.0.1 ::1
23
+
24
+###
25
+# TRACIM SPECIFIC CONF
26
+###
27
+
28
+### Global
29
+debug = false
30
+cache_dir = %(here)s/data
31
+# preview generator cache directory
32
+preview_cache_dir = /tmp/tracim/preview/
33
+# file depot storage
34
+depot_storage_name = tracim
35
+depot_storage_dir = %(here)s/depot/
36
+
37
+# The following parameters allow to personalize the home page
38
+# They are html ready (you can put html tags they will be interpreted)
39
+website.title = TRACIM
40
+website.title.color = #555
41
+website.home.subtitle = Default login: email: admin@admin.admin (password: admin@admin.admin)
42
+website.home.tag_line = <div class="text-center" style="font-weight: bold;">Collaboration, versionning and traceability</div>
43
+website.home.below_login_form = in case of problem, please contact the administrator.
44
+# Values may be 'all' or 'folders'
45
+website.treeview.content = all
46
+# The following base_url is used for links and icons
47
+# integrated in the email notifcations
48
+website.base_url = http://127.0.0.1:8080
49
+# If config not provided, it will be extracted from website.base_url
50
+website.server_name = 127.0.0.1
51
+
52
+# Specifies if the update of comments and attached files is allowed (by the owner only).
53
+# Examples:
54
+#    600 means 10 minutes (ie 600 seconds)
55
+#   3600 means 1 hour (60x60 seconds)
56
+#
57
+# Allowed values:
58
+#  -1 means that content update is allowed for ever
59
+#   0 means that content update is not allowed
60
+#   x means that content update is allowed for x seconds (with x>0)
61
+content.update.allowed.duration = 3600
62
+
63
+# Auth type (internal or ldap)
64
+auth_type = internal
65
+# If auth_type is ldap, uncomment following ldap_* parameters
66
+# LDAP server address
67
+# ldap_url = ldap://localhost:389
68
+# Base dn to make queries
69
+# ldap_base_dn = dc=directory,dc=fsf,dc=org
70
+# Bind dn to identify the search
71
+# ldap_bind_dn = cn=admin,dc=directory,dc=fsf,dc=org
72
+# The bind password
73
+# ldap_bind_pass = toor
74
+# Attribute name of user record who contain user login (email)
75
+# ldap_ldap_naming_attribute = uid
76
+# Matching between ldap attribute and ldap user field (ldap_attr1=user_field1,ldap_attr2=user_field2,...)
77
+# ldap_user_attributes = mail=email
78
+# TLS usage to communicate with your LDAP server
79
+# ldap_tls = False
80
+# If True, LDAP own tracim group managment (not available for now!)
81
+# ldap_group_enabled = False
82
+# User auth token validity in seconds (used to interfaces like web calendars)
83
+user.auth_token.validity = 604800
84
+
85
+### Mail
86
+
87
+# Reset password through email related configuration.
88
+# These emails will be sent through SMTP
89
+#
90
+resetpassword.email_sender = email@sender.com
91
+resetpassword.smtp_host = smtp.sender
92
+resetpassword.smtp_port = 25
93
+resetpassword.smtp_login = smtp.login
94
+resetpassword.smtp_passwd = smtp.password
95
+
96
+email.notification.activated = False
97
+# email.notification.log_file_path = /tmp/mail-notifications.log
98
+# email notifications can be sent with the user_id added as an identifier
99
+# this way email clients like Thunderbird will be able to distinguish
100
+# notifications generated by a user or another one
101
+email.notification.from.email = noreply+{user_id}@trac.im
102
+email.notification.from.default_label = Tracim Notifications
103
+email.notification.reply_to.email = reply+{content_id}@trac.im
104
+email.notification.references.email = thread+{content_id}@trac.im
105
+email.notification.content_update.template.html = %(here)s/tracim/templates/mail/content_update_body_html.mak
106
+email.notification.content_update.template.text = %(here)s/tracim/templates/mail/content_update_body_text.mak
107
+email.notification.created_account.template.html = %(here)s/tracim/templates/mail/created_account_body_html.mak
108
+email.notification.created_account.template.text = %(here)s/tracim/templates/mail/created_account_body_text.mak
109
+# Note: items between { and } are variable names. Do not remove / rename them
110
+email.notification.content_update.subject = [{website_title}] [{workspace_label}] {content_label} ({content_status_label})
111
+email.notification.created_account.subject = [{website_title}] Created account
112
+# processing_mode may be sync or async
113
+email.notification.processing_mode = sync
114
+email.notification.smtp.server = your_smtp_server
115
+email.notification.smtp.port = 25
116
+email.notification.smtp.user = your_smtp_user
117
+email.notification.smtp.password = your_smtp_password
118
+
119
+## Email sending configuration
120
+# processing_mode may be sync or async,
121
+# with async, please configure redis below
122
+email.processing_mode = sync
123
+# email.async.redis.host = localhost
124
+# email.async.redis.port = 6379
125
+# email.async.redis.db = 0
126
+
127
+# Email reply configuration
128
+email.reply.activated = False
129
+email.reply.imap.server = your_imap_server
130
+email.reply.imap.port = 993
131
+email.reply.imap.user = your_imap_user
132
+email.reply.imap.password = your_imap_password
133
+email.reply.imap.folder = INBOX
134
+email.reply.imap.use_ssl = true
135
+email.reply.imap.use_idle = true
136
+# Re-new connection each 10 minutes
137
+email.reply.connection.max_lifetime = 600
138
+# Token for communication between mail fetcher and tracim controller
139
+email.reply.token = mysecuretoken
140
+# Delay in seconds between each check
141
+email.reply.check.heartbeat = 60
142
+email.reply.use_html_parsing = true
143
+email.reply.use_txt_parsing = true
144
+# Lockfile path is required for email_reply feature,
145
+# it's just an empty file use to prevent concurrent access to imap unseen mail
146
+email.reply.lockfile_path = %(here)s/email_fetcher.lock
147
+
148
+### Radical (CalDav server) configuration
149
+
150
+# radicale.server.host = 0.0.0.0
151
+# radicale.server.port = 5232
152
+# radicale.server.ssl = false
153
+radicale.server.filesystem.folder = %(here)s/radicale/collections/
154
+# radicale.server.allow_origin = *
155
+# radicale.server.realm_message = Tracim Calendar - Password Required
156
+## url can be extended like http://127.0.0.1:5232/calendar
157
+## in this case, you have to create your own proxy behind this url.
158
+## and update following parameters
159
+# radicale.client.base_url.host = http://127.0.0.1:5232
160
+# radicale.client.base_url.prefix = /
161
+
162
+### WSGIDAV
163
+
164
+wsgidav.config_path = %(here)s/wsgidav.conf
165
+## url can be extended like 127.0.0.1/webdav
166
+## in this case, you have to create your own proxy behind this url.
167
+## Do not set http:// prefix.
168
+# wsgidav.client.base_url = 127.0.0.1:<WSGIDAV_PORT>
169
+
170
+###
171
+# wsgi server configuration
172
+###
173
+
174
+[server:main]
175
+use = egg:waitress#main
176
+listen = localhost:6543
177
+
178
+[alembic]
179
+# path to migration scripts
180
+script_location = tracim/migration
181
+
182
+# template used to generate migration files
183
+# file_template = %%(rev)s_%%(slug)s
184
+
185
+# timezone to use when rendering the date
186
+# within the migration file as well as the filename.
187
+# string value is passed to dateutil.tz.gettz()
188
+# leave blank for localtime
189
+# timezone =
190
+
191
+# max length of characters to apply to the
192
+# "slug" field
193
+#truncate_slug_length = 40
194
+
195
+# set to 'true' to run the environment during
196
+# the 'revision' command, regardless of autogenerate
197
+# revision_environment = false
198
+
199
+# set to 'true' to allow .pyc and .pyo files without
200
+# a source .py file to be detected as revisions in the
201
+# versions/ directory
202
+# sourceless = false
203
+
204
+# version location specification; this defaults
205
+# to migrate/versions.  When using multiple version
206
+# directories, initial revisions must be specified with --version-path
207
+# version_locations = %(here)s/bar %(here)s/bat migrate/versions
208
+
209
+# the output encoding used when revision files
210
+# are written from script.py.mako
211
+# output_encoding = utf-8
212
+
213
+sqlalchemy.url = sqlite:///%(here)s/tracim.sqlite
214
+
215
+###
216
+# logging configuration
217
+# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
218
+###
219
+
220
+[loggers]
221
+keys = root, tracim, sqlalchemy, alembic
222
+
223
+[handlers]
224
+keys = console
225
+
226
+[formatters]
227
+keys = generic
228
+
229
+[logger_root]
230
+level = INFO
231
+handlers = console
232
+
233
+[logger_tracim]
234
+level = DEBUG
235
+handlers =
236
+qualname = tracim
237
+
238
+[logger_sqlalchemy]
239
+level = INFO
240
+handlers =
241
+qualname = sqlalchemy.engine
242
+# "level = INFO" logs SQL queries.
243
+# "level = DEBUG" logs SQL queries and results.
244
+# "level = WARN" logs neither.  (Recommended for production systems.)
245
+
246
+[logger_alembic]
247
+level = INFO
248
+handlers =
249
+qualname = alembic
250
+
251
+[handler_console]
252
+class = StreamHandler
253
+args = (sys.stderr,)
254
+level = NOTSET
255
+formatter = generic
256
+
257
+[formatter_generic]
258
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
259
+datefmt = %H:%M:%S

+ 2 - 2
tracim/command/user.py View File

@@ -12,7 +12,7 @@ from tracim.command import Extender
12 12
 #from tracim.lib.daemons import DaemonsManager
13 13
 #from tracim.lib.daemons import RadicaleDaemon
14 14
 #from tracim.lib.email import get_email_manager
15
-from tracim.exceptions import UserAlreadyExistError, GroupNotExist
15
+from tracim.exceptions import UserAlreadyExistError, GroupDoesNotExist
16 16
 from tracim.exceptions import NotificationNotSend
17 17
 from tracim.exceptions import BadCommandError
18 18
 from tracim.lib.core.group import GroupApi
@@ -93,7 +93,7 @@ class UserCommand(AppContextCommand):
93 93
             for group in groups_availables:
94 94
                 msg+= "'{}',".format(group)
95 95
             self._session.rollback()
96
-            raise GroupNotExist(msg)
96
+            raise GroupDoesNotExist(msg)
97 97
         return self._group_api.get_one_with_name(name)
98 98
 
99 99
     def _add_user_to_named_group(

+ 7 - 3
tracim/exceptions.py View File

@@ -89,13 +89,17 @@ class WrongUserPassword(TracimException):
89 89
     pass
90 90
 
91 91
 
92
-class UserNotExist(TracimException):
92
+class NotificationNotSend(TracimException):
93 93
     pass
94 94
 
95 95
 
96
-class NotificationNotSend(TracimException):
96
+class GroupDoesNotExist(TracimError):
97
+    pass
98
+
99
+
100
+class UserDoesNotExist(TracimException):
97 101
     pass
98 102
 
99 103
 
100
-class GroupNotExist(TracimError):
104
+class UserNotFoundInTracimRequest(TracimException):
101 105
     pass

+ 3 - 4
tracim/lib/core/group.py View File

@@ -3,7 +3,7 @@ import typing
3 3
 
4 4
 from sqlalchemy.orm.exc import NoResultFound
5 5
 
6
-from tracim.exceptions import GroupNotExist
6
+from tracim.exceptions import GroupDoesNotExist
7 7
 from tracim import CFG
8 8
 
9 9
 
@@ -21,7 +21,6 @@ class GroupApi(object):
21 21
             session: Session,
22 22
             current_user: typing.Optional[User],
23 23
             config: CFG
24
-
25 24
     ):
26 25
         self._user = current_user
27 26
         self._session = session
@@ -35,14 +34,14 @@ class GroupApi(object):
35 34
             group = self._base_query().filter(Group.group_id == group_id).one()
36 35
             return group
37 36
         except NoResultFound:
38
-            raise GroupNotExist()
37
+            raise GroupDoesNotExist()
39 38
 
40 39
     def get_one_with_name(self, group_name) -> Group:
41 40
         try:
42 41
             group = self._base_query().filter(Group.group_name == group_name).one()
43 42
             return group
44 43
         except NoResultFound:
45
-            raise GroupNotExist()
44
+            raise GroupDoesNotExist()
46 45
 
47 46
     def get_all(self):
48 47
         return self._base_query().order_by(Group.group_id).all()

+ 11 - 10
tracim/lib/core/user.py View File

@@ -10,9 +10,10 @@ from tracim.lib.mail_notifier.notifier import get_email_manager
10 10
 from sqlalchemy.orm import Session
11 11
 
12 12
 from tracim import CFG
13
-from tracim.models.auth import User, Group
13
+from tracim.models.auth import User
14
+from tracim.models.auth import Group
14 15
 from sqlalchemy.orm.exc import NoResultFound
15
-from tracim.exceptions import WrongUserPassword, UserNotExist
16
+from tracim.exceptions import WrongUserPassword, UserDoesNotExist
16 17
 from tracim.exceptions import AuthenticationFailed
17 18
 from tracim.models.context_models import UserInContext
18 19
 
@@ -51,8 +52,8 @@ class UserApi(object):
51 52
         """
52 53
         try:
53 54
             user = self._base_query().filter(User.user_id == user_id).one()
54
-        except NoResultFound:
55
-            raise UserNotExist()
55
+        except NoResultFound as exc:
56
+            raise UserDoesNotExist('User "{}" not found in database'.format(user_id)) from exc  # nopep8
56 57
         return user
57 58
 
58 59
     def get_one_by_email(self, email: str) -> User:
@@ -63,8 +64,8 @@ class UserApi(object):
63 64
         """
64 65
         try:
65 66
             user = self._base_query().filter(User.email == email).one()
66
-        except NoResultFound:
67
-            raise UserNotExist()
67
+        except NoResultFound as exc:
68
+            raise UserDoesNotExist('User "{}" not found in database'.format(email)) from exc  # nopep8
68 69
         return user
69 70
 
70 71
     # FIXME - G.M - 24-04-2018 - Duplicate method with get_one.
@@ -76,7 +77,7 @@ class UserApi(object):
76 77
         Get current_user
77 78
         """
78 79
         if not self._user:
79
-            raise UserNotExist()
80
+            raise UserDoesNotExist('There is no current user')
80 81
         return self._user
81 82
 
82 83
     def get_all(self) -> typing.Iterable[User]:
@@ -105,9 +106,9 @@ class UserApi(object):
105 106
             if user.validate_password(password):
106 107
                 return user
107 108
             else:
108
-                raise WrongUserPassword()
109
-        except (WrongUserPassword, NoResultFound, UserNotExist):
110
-            raise AuthenticationFailed()
109
+                raise WrongUserPassword('User "{}" password is incorrect'.format(email))  # nopep8
110
+        except (WrongUserPassword, UserDoesNotExist) as exc:
111
+            raise AuthenticationFailed('User "{}" authentication failed'.format(email)) from exc  # nopep8
111 112
 
112 113
     # Actions
113 114
 

+ 29 - 22
tracim/lib/core/userworkspace.py View File

@@ -7,7 +7,8 @@ from tracim.models.context_models import UserRoleWorkspaceInContext
7 7
 __author__ = 'damien'
8 8
 
9 9
 from sqlalchemy.orm import Session
10
-from tracim.models.auth import User, Group
10
+from sqlalchemy.orm import Query
11
+from tracim.models.auth import User
11 12
 from tracim.models.data import Workspace
12 13
 from tracim.models.data import UserRoleInWorkspace
13 14
 from tracim.models.data import RoleType
@@ -73,15 +74,17 @@ class RoleApi(object):
73 74
 
74 75
         return role
75 76
 
76
-    def __init__(self,
77
-                 session: Session,
78
-                 current_user: typing.Optional[User],
79
-                 config: CFG):
77
+    def __init__(
78
+        self,
79
+        session: Session,
80
+        current_user: typing.Optional[User],
81
+        config: CFG,
82
+    )-> None:
80 83
         self._session = session
81 84
         self._user = current_user
82 85
         self._config = config
83 86
 
84
-    def _get_one_rsc(self, user_id, workspace_id):
87
+    def _get_one_rsc(self, user_id: int, workspace_id: int) -> Query:
85 88
         """
86 89
         :param user_id:
87 90
         :param workspace_id:
@@ -91,16 +94,16 @@ class RoleApi(object):
91 94
             filter(UserRoleInWorkspace.workspace_id == workspace_id).\
92 95
             filter(UserRoleInWorkspace.user_id == user_id)
93 96
 
94
-    def get_one(self, user_id, workspace_id):
97
+    def get_one(self, user_id: int, workspace_id: int) -> UserRoleInWorkspace:
95 98
         return self._get_one_rsc(user_id, workspace_id).one()
96 99
 
97 100
     def create_one(
98
-            self,
99
-            user: User,
100
-            workspace: Workspace,
101
-            role_level: int,
102
-            with_notif: bool,
103
-            flush: bool=True
101
+        self,
102
+        user: User,
103
+        workspace: Workspace,
104
+        role_level: int,
105
+        with_notif: bool,
106
+        flush: bool=True
104 107
     ) -> UserRoleInWorkspace:
105 108
         role = self.create_role()
106 109
         role.user_id = user.user_id
@@ -111,34 +114,38 @@ class RoleApi(object):
111 114
             self._session.flush()
112 115
         return role
113 116
 
114
-    def delete_one(self, user_id, workspace_id, flush=True):
117
+    def delete_one(self, user_id: int, workspace_id: int, flush=True) -> None:
115 118
         self._get_one_rsc(user_id, workspace_id).delete()
116 119
         if flush:
117 120
             self._session.flush()
118 121
 
119
-    def _get_all_for_user(self, user_id):
122
+    def _get_all_for_user(self, user_id) -> typing.List[UserRoleInWorkspace]:
120 123
         return self._session.query(UserRoleInWorkspace)\
121 124
             .filter(UserRoleInWorkspace.user_id == user_id)
122 125
 
123
-    def get_all_for_user(self, user: User):
126
+    def get_all_for_user(self, user: User) -> typing.List[UserRoleInWorkspace]:
124 127
         return self._get_all_for_user(user.user_id).all()
125 128
 
126 129
     def get_all_for_user_order_by_workspace(
127
-            self,
128
-            user_id: int
129
-    ) -> UserRoleInWorkspace:
130
+        self,
131
+        user_id: int
132
+    ) -> typing.List[UserRoleInWorkspace]:
130 133
         return self._get_all_for_user(user_id)\
131 134
             .join(UserRoleInWorkspace.workspace).order_by(Workspace.label).all()
132 135
 
133
-    def get_all_for_workspace(self, workspace:Workspace):
136
+    def get_all_for_workspace(
137
+        self,
138
+        workspace:Workspace
139
+    ) -> typing.List[UserRoleInWorkspace]:
134 140
         return self._session.query(UserRoleInWorkspace)\
135 141
             .filter(UserRoleInWorkspace.workspace_id == workspace.workspace_id).all()  # nopep8
136 142
 
137
-    def save(self, role: UserRoleInWorkspace):
143
+    def save(self, role: UserRoleInWorkspace) -> None:
138 144
         self._session.flush()
139 145
 
146
+    # TODO - G.M - 07-06-2018 - [Cleanup] Check if this method is already needed
140 147
     @classmethod
141
-    def get_roles_for_select_field(cls):
148
+    def get_roles_for_select_field(cls) -> typing.List[RoleType]:
142 149
         """
143 150
 
144 151
         :return: list of DictLikeClass instances representing available Roles

+ 2 - 2
tracim/lib/utils/authentification.py View File

@@ -4,7 +4,7 @@ from pyramid.request import Request
4 4
 from sqlalchemy.orm.exc import NoResultFound
5 5
 
6 6
 from tracim import TracimRequest
7
-from tracim.exceptions import UserNotExist
7
+from tracim.exceptions import UserDoesNotExist
8 8
 from tracim.lib.core.user import UserApi
9 9
 from tracim.models import User
10 10
 
@@ -51,6 +51,6 @@ def _get_basic_auth_unsafe_user(
51 51
         if not login:
52 52
             return None
53 53
         user = uapi.get_one_by_email(login)
54
-    except (NoResultFound, UserNotExist):
54
+    except (NoResultFound, UserDoesNotExist):
55 55
         return None
56 56
     return user

+ 1 - 1
tracim/lib/utils/authorization.py View File

@@ -100,4 +100,4 @@ def require_workspace_role(minimal_required_role):
100 100
             raise InsufficientUserWorkspaceRole()
101 101
 
102 102
         return wrapper
103
-    return decorator
103
+    return decorator

+ 10 - 8
tracim/lib/utils/request.py View File

@@ -5,7 +5,10 @@ TracimRequest and related functions
5 5
 from pyramid.request import Request
6 6
 from sqlalchemy.orm.exc import NoResultFound
7 7
 
8
-from tracim.exceptions import NotAuthentificated, UserNotExist
8
+
9
+from tracim.exceptions import NotAuthentificated
10
+from tracim.exceptions import UserNotFoundInTracimRequest
11
+from tracim.exceptions import UserDoesNotExist
9 12
 from tracim.exceptions import WorkspaceNotFound
10 13
 from tracim.exceptions import ImmutableAttribute
11 14
 from tracim.lib.core.user import UserApi
@@ -142,10 +145,10 @@ def get_candidate_user(
142 145
         if 'user_id' in request.matchdict:
143 146
             login = request.matchdict['user_id']
144 147
         if not login:
145
-            raise UserNotExist('no user_id found, incorrect request ?')
148
+            raise UserNotFoundInTracimRequest('You request a candidate user but the context not permit to found one')  # nopep8
146 149
         user = uapi.get_one(login)
147
-    except NoResultFound:
148
-        raise NotAuthentificated('User not found')
150
+    except UserNotFoundInTracimRequest as exc:
151
+        raise UserDoesNotExist('User {} not found'.format(login)) from exc
149 152
     return user
150 153
 
151 154
 
@@ -162,11 +165,10 @@ def get_auth_safe_user(
162 165
     try:
163 166
         login = request.authenticated_userid
164 167
         if not login:
165
-            raise NotAuthentificated('not authenticated user_id,'
166
-                                     'Failed Authentification ?')
168
+            raise UserNotFoundInTracimRequest('You request a current user but the context not permit to found one')  # nopep8
167 169
         user = uapi.get_one_by_email(login)
168
-    except NoResultFound:
169
-        raise NotAuthentificated('User not found')
170
+    except (UserDoesNotExist, UserNotFoundInTracimRequest) as exc:
171
+        raise NotAuthentificated('User {} not found'.format(login)) from exc
170 172
     return user
171 173
 
172 174
 

+ 10 - 10
tracim/models/applications.py View File

@@ -25,7 +25,6 @@ class Application(object):
25 25
         self.main_route = main_route
26 26
 
27 27
 
28
-# TODO - G.M - 21-05-2018 Do not rely on hardcoded app list
29 28
 # default apps
30 29
 calendar = Application(
31 30
     label='Calendar',
@@ -58,29 +57,30 @@ file = Application(
58 57
     main_route='/#/workspaces/{workspace_id}/contents?type=file',
59 58
 )
60 59
 
61
-pagemarkdownplus = Application(
62
-    label='Rich Markdown Files',  # TODO - G.M - 24-05-2018 - Check label
63
-    slug='contents/pagemarkdownplus',
60
+markdownpluspage = Application(
61
+    label='Markdown Plus Documents',  # TODO - G.M - 24-05-2018 - Check label
62
+    slug='contents/markdownpluspage',
64 63
     icon='file-code',
65 64
     hexcolor='#f12d2d',
66 65
     is_active=True,
67 66
     config={},
68
-    main_route='/#/workspaces/{workspace_id}/contents?type=pagemarkdownplus',
67
+    main_route='/#/workspaces/{workspace_id}/contents?type=markdownpluspage',
69 68
 )
70 69
 
71
-pagehtml = Application(
70
+htmlpage = Application(
72 71
     label='Text Documents',  # TODO - G.M - 24-05-2018 - Check label
73
-    slug='contents/pagehtml',
72
+    slug='contents/htmlpage',
74 73
     icon='file-text-o',
75 74
     hexcolor='#3f52e3',
76 75
     is_active=True,
77 76
     config={},
78
-    main_route='/#/workspaces/{workspace_id}/contents?type=pagehtml',
77
+    main_route='/#/workspaces/{workspace_id}/contents?type=htmlpage',
79 78
 )
79
+# TODO - G.M - 08-06-2018 - This is hardcoded lists of app, make this dynamic.
80 80
 # List of applications
81 81
 applications = [
82
-    pagehtml,
83
-    pagemarkdownplus,
82
+    htmlpage,
83
+    markdownpluspage,
84 84
     file,
85 85
     thread,
86 86
     calendar,

+ 13 - 9
tracim/models/auth.py View File

@@ -91,15 +91,19 @@ class Group(DeclarativeBase):
91 91
 class Profile(object):
92 92
     """This model is the "max" group associated to a given user."""
93 93
 
94
-    _NAME = [Group.TIM_NOBODY_GROUPNAME,
95
-             Group.TIM_USER_GROUPNAME,
96
-             Group.TIM_MANAGER_GROUPNAME,
97
-             Group.TIM_ADMIN_GROUPNAME]
98
-
99
-    _IDS = [Group.TIM_NOBODY,
100
-            Group.TIM_USER,
101
-            Group.TIM_MANAGER,
102
-            Group.TIM_ADMIN]
94
+    _NAME = [
95
+        Group.TIM_NOBODY_GROUPNAME,
96
+        Group.TIM_USER_GROUPNAME,
97
+        Group.TIM_MANAGER_GROUPNAME,
98
+        Group.TIM_ADMIN_GROUPNAME,
99
+    ]
100
+
101
+    _IDS = [
102
+        Group.TIM_NOBODY,
103
+        Group.TIM_USER,
104
+        Group.TIM_MANAGER,
105
+        Group.TIM_ADMIN,
106
+    ]
103 107
 
104 108
     # TODO - G.M - 18-04-2018 [Cleanup] Drop this
105 109
     # _LABEL = [l_('Nobody'),

+ 2 - 2
tracim/models/context_models.py View File

@@ -8,8 +8,8 @@ from tracim import CFG
8 8
 from tracim.models import User
9 9
 from tracim.models.auth import Profile
10 10
 from tracim.models.data import Workspace, UserRoleInWorkspace
11
-from tracim.models.workspace_menu_entries import default_workspace_menu_entry, \
12
-    WorkspaceMenuEntry
11
+from tracim.models.workspace_menu_entries import default_workspace_menu_entry
12
+from tracim.models.workspace_menu_entries import WorkspaceMenuEntry
13 13
 
14 14
 
15 15
 class LoginCredentials(object):

+ 7 - 7
tracim/models/data.py View File

@@ -131,13 +131,13 @@ class UserRoleInWorkspace(DeclarativeBase):
131 131
     WORKSPACE_MANAGER = 8
132 132
 
133 133
     # TODO - G.M - 10-04-2018 - [Cleanup] Drop this
134
-
135
-    SLUG = dict()
136
-    SLUG[NOT_APPLICABLE] = 'not_applicable'
137
-    SLUG[READER] = 'reader'
138
-    SLUG[CONTRIBUTOR] = 'contributor'
139
-    SLUG[CONTENT_MANAGER] = 'content_manager'
140
-    SLUG[WORKSPACE_MANAGER] = 'workspace_manager'
134
+    SLUG = {
135
+        NOT_APPLICABLE: 'not_applicable',
136
+        READER: 'reader',
137
+        CONTRIBUTOR: 'contributor',
138
+        CONTENT_MANAGER: 'content_manager',
139
+        WORKSPACE_MANAGER: 'workspace_manager',
140
+    }
141 141
 
142 142
     LABEL = dict()
143 143
     LABEL[0] = l_('N/A')

+ 2 - 2
tracim/models/workspace_menu_entries.py View File

@@ -24,7 +24,6 @@ class WorkspaceMenuEntry(object):
24 24
         self.hexcolor = hexcolor
25 25
         self.icon = icon
26 26
 
27
-
28 27
 dashboard_menu_entry = WorkspaceMenuEntry(
29 28
   slug='dashboard',
30 29
   label='Dashboard',
@@ -40,7 +39,8 @@ all_content_menu_entry = WorkspaceMenuEntry(
40 39
   icon="",
41 40
 )
42 41
 
43
-
42
+# TODO - G.M - 08-06-2018 - This is hardcoded default menu entry,
43
+#  of app, make this dynamic (and loaded from application system)
44 44
 def default_workspace_menu_entry(
45 45
     workspace: Workspace,
46 46
 )-> typing.List[WorkspaceMenuEntry]:

+ 7 - 11
tracim/tests/commands/test_commands.py View File

@@ -1,17 +1,13 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 import os
3 3
 import subprocess
4
-
5 4
 import pytest
6
-import transaction
7
-from pkg_resources import load_entry_point
8
-from sqlalchemy.orm.exc import NoResultFound
9
-
10 5
 import tracim
11 6
 from tracim.command import TracimCLI
12
-from tracim.command.user import UserCommand
13
-from tracim.exceptions import UserAlreadyExistError, BadCommandError, \
14
-    GroupNotExist, UserNotExist
7
+from tracim.exceptions import UserAlreadyExistError
8
+from tracim.exceptions import BadCommandError
9
+from tracim.exceptions import GroupDoesNotExist
10
+from tracim.exceptions import UserDoesNotExist
15 11
 from tracim.lib.core.user import UserApi
16 12
 from tracim.tests import CommandFunctionalTest
17 13
 
@@ -48,7 +44,7 @@ class TestCommands(CommandFunctionalTest):
48 44
             session=self.session,
49 45
             config=self.app_config,
50 46
         )
51
-        with pytest.raises(UserNotExist):
47
+        with pytest.raises(UserDoesNotExist):
52 48
             api.get_one_by_email('command_test@user')
53 49
         app = TracimCLI()
54 50
         result = app.run([
@@ -72,7 +68,7 @@ class TestCommands(CommandFunctionalTest):
72 68
             session=self.session,
73 69
             config=self.app_config,
74 70
         )
75
-        with pytest.raises(UserNotExist):
71
+        with pytest.raises(UserDoesNotExist):
76 72
             api.get_one_by_email('command_test@user')
77 73
         app = TracimCLI()
78 74
         result = app.run([
@@ -98,7 +94,7 @@ class TestCommands(CommandFunctionalTest):
98 94
             config=self.app_config,
99 95
         )
100 96
         app = TracimCLI()
101
-        with pytest.raises(GroupNotExist):
97
+        with pytest.raises(GroupDoesNotExist):
102 98
             result = app.run([
103 99
                 'user', 'create',
104 100
                 '-c', 'tests_configs.ini#command_test',

+ 0 - 2
tracim/tests/functional/test_session.py View File

@@ -50,7 +50,6 @@ class TestLoginEndpoint(FunctionalTest):
50 50
         assert res.json_body['created']
51 51
         assert res.json_body['is_active']
52 52
         assert res.json_body['profile']
53
-        assert isinstance(res.json_body['profile']['id'], int)
54 53
         assert res.json_body['profile']['slug'] == 'administrators'
55 54
         assert res.json_body['caldav_url'] is None
56 55
         assert res.json_body['avatar_url'] is None
@@ -109,7 +108,6 @@ class TestWhoamiEndpoint(FunctionalTest):
109 108
         assert res.json_body['created']
110 109
         assert res.json_body['is_active']
111 110
         assert res.json_body['profile']
112
-        assert isinstance(res.json_body['profile']['id'], int)
113 111
         assert res.json_body['profile']['slug'] == 'administrators'
114 112
         assert res.json_body['caldav_url'] is None
115 113
         assert res.json_body['avatar_url'] is None

+ 3 - 3
tracim/tests/functional/test_system.py View File

@@ -25,14 +25,14 @@ class TestApplicationsEndpoint(FunctionalTest):
25 25
         res = res.json_body
26 26
         application = res[0]
27 27
         assert application['label'] == "Text Documents"
28
-        assert application['slug'] == 'contents/pagehtml'
28
+        assert application['slug'] == 'contents/htmlpage'
29 29
         assert application['icon'] == 'file-text-o'
30 30
         assert application['hexcolor'] == '#3f52e3'
31 31
         assert application['is_active'] is True
32 32
         assert 'config' in application
33 33
         application = res[1]
34
-        assert application['label'] == "Rich Markdown Files"
35
-        assert application['slug'] == 'contents/pagemarkdownplus'
34
+        assert application['label'] == "Markdown Plus Documents"
35
+        assert application['slug'] == 'contents/markdownpluspage'
36 36
         assert application['icon'] == 'file-code'
37 37
         assert application['hexcolor'] == '#f12d2d'
38 38
         assert application['is_active'] is True

+ 5 - 5
tracim/tests/functional/test_user.py View File

@@ -47,16 +47,16 @@ class TestUserWorkspaceEndpoint(FunctionalTest):
47 47
         assert sidebar_entry['icon'] == ""
48 48
 
49 49
         sidebar_entry = workspace['sidebar_entries'][2]
50
-        assert sidebar_entry['slug'] == 'contents/pagehtml'
50
+        assert sidebar_entry['slug'] == 'contents/htmlpage'
51 51
         assert sidebar_entry['label'] == 'Text Documents'
52
-        assert sidebar_entry['route'] == '/#/workspaces/1/contents?type=pagehtml'  # nopep8
52
+        assert sidebar_entry['route'] == '/#/workspaces/1/contents?type=htmlpage'  # nopep8
53 53
         assert sidebar_entry['hexcolor'] == "#3f52e3"
54 54
         assert sidebar_entry['icon'] == "file-text-o"
55 55
 
56 56
         sidebar_entry = workspace['sidebar_entries'][3]
57
-        assert sidebar_entry['slug'] == 'contents/pagemarkdownplus'
58
-        assert sidebar_entry['label'] == 'Rich Markdown Files'
59
-        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=pagemarkdownplus"    # nopep8
57
+        assert sidebar_entry['slug'] == 'contents/markdownpluspage'
58
+        assert sidebar_entry['label'] == 'Markdown Plus Documents'
59
+        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=markdownpluspage"    # nopep8
60 60
         assert sidebar_entry['hexcolor'] == "#f12d2d"
61 61
         assert sidebar_entry['icon'] == "file-code"
62 62
 

+ 5 - 6
tracim/tests/functional/test_workspaces.py View File

@@ -48,16 +48,16 @@ class TestWorkspaceEndpoint(FunctionalTest):
48 48
         assert sidebar_entry['icon'] == ""
49 49
 
50 50
         sidebar_entry = workspace['sidebar_entries'][2]
51
-        assert sidebar_entry['slug'] == 'contents/pagehtml'
51
+        assert sidebar_entry['slug'] == 'contents/htmlpage'
52 52
         assert sidebar_entry['label'] == 'Text Documents'
53
-        assert sidebar_entry['route'] == '/#/workspaces/1/contents?type=pagehtml'  # nopep8
53
+        assert sidebar_entry['route'] == '/#/workspaces/1/contents?type=htmlpage'  # nopep8
54 54
         assert sidebar_entry['hexcolor'] == "#3f52e3"
55 55
         assert sidebar_entry['icon'] == "file-text-o"
56 56
 
57 57
         sidebar_entry = workspace['sidebar_entries'][3]
58
-        assert sidebar_entry['slug'] == 'contents/pagemarkdownplus'
59
-        assert sidebar_entry['label'] == 'Rich Markdown Files'
60
-        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=pagemarkdownplus"    # nopep8
58
+        assert sidebar_entry['slug'] == 'contents/markdownpluspage'
59
+        assert sidebar_entry['label'] == 'Markdown Plus Documents'
60
+        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=markdownpluspage"    # nopep8
61 61
         assert sidebar_entry['hexcolor'] == "#f12d2d"
62 62
         assert sidebar_entry['icon'] == "file-code"
63 63
 
@@ -156,7 +156,6 @@ class TestWorkspaceMembersEndpoint(FunctionalTest):
156 156
         assert len(res) == 2
157 157
         user_role = res[0]
158 158
         assert user_role['role_slug'] == 'workspace_manager'
159
-        assert user_role['role_id'] == 8
160 159
         assert user_role['user_id'] == 1
161 160
         assert user_role['workspace_id'] == 1
162 161
         assert user_role['user']['display_name'] == 'Global manager'

+ 3 - 3
tracim/tests/library/test_group_api.py View File

@@ -1,7 +1,7 @@
1 1
 # coding=utf-8
2 2
 import pytest
3 3
 
4
-from tracim.exceptions import GroupNotExist
4
+from tracim.exceptions import GroupDoesNotExist
5 5
 from tracim.lib.core.group import GroupApi
6 6
 from tracim.tests import DefaultTest
7 7
 from tracim.fixtures.users_and_groups import Base as BaseFixture
@@ -33,7 +33,7 @@ class TestGroupApi(DefaultTest):
33 33
             session=self.session,
34 34
             config=self.app_config,
35 35
         )
36
-        with pytest.raises(GroupNotExist):
36
+        with pytest.raises(GroupDoesNotExist):
37 37
             group = api.get_one(10)
38 38
 
39 39
     def test_unit__get_one_group_with_name__nominal_case(self) -> None:
@@ -58,7 +58,7 @@ class TestGroupApi(DefaultTest):
58 58
             session=self.session,
59 59
             config=self.app_config,
60 60
         )
61
-        with pytest.raises(GroupNotExist):
61
+        with pytest.raises(GroupDoesNotExist):
62 62
             group = api.get_one_with_name('unknown_group')
63 63
 
64 64
     def test_unit__get_all__ok__nominal_case(self):

+ 3 - 3
tracim/tests/library/test_user_api.py View File

@@ -4,7 +4,7 @@ from sqlalchemy.orm.exc import NoResultFound
4 4
 
5 5
 import transaction
6 6
 
7
-from tracim.exceptions import UserNotExist, AuthenticationFailed
7
+from tracim.exceptions import UserDoesNotExist, AuthenticationFailed
8 8
 from tracim.lib.core.user import UserApi
9 9
 from tracim.models import User
10 10
 from tracim.models.context_models import UserInContext
@@ -91,7 +91,7 @@ class TestUserApi(DefaultTest):
91 91
             session=self.session,
92 92
             config=self.config,
93 93
         )
94
-        with pytest.raises(UserNotExist):
94
+        with pytest.raises(UserDoesNotExist):
95 95
             api.get_one_by_email('unknown')
96 96
 
97 97
     def test_unit__get_all__ok__nominal_case(self):
@@ -158,7 +158,7 @@ class TestUserApi(DefaultTest):
158 158
             session=self.session,
159 159
             config=self.config,
160 160
         )
161
-        with pytest.raises(UserNotExist):
161
+        with pytest.raises(UserDoesNotExist):
162 162
             api.get_current_user()
163 163
 
164 164
     def test_unit__authenticate_user___ok__nominal_case(self):

+ 115 - 40
tracim/views/core_api/schemas.py View File

@@ -9,44 +9,84 @@ from tracim.models.data import UserRoleInWorkspace
9 9
 
10 10
 
11 11
 class ProfileSchema(marshmallow.Schema):
12
-    id = marshmallow.fields.Int(dump_only=True, validate=OneOf(Profile._IDS))
13
-    slug = marshmallow.fields.String(attribute='name', validate=OneOf(Profile._NAME))
12
+    slug = marshmallow.fields.String(
13
+        attribute='name',
14
+        validate=OneOf(Profile._NAME),
15
+        example='managers',
16
+    )
17
+
18
+    class Meta:
19
+        description = 'User Profile, give user right on whole Tracim instance.'
14 20
 
15 21
 
16 22
 class UserSchema(marshmallow.Schema):
17 23
 
18
-    user_id = marshmallow.fields.Int(dump_only=True)
19
-    email = marshmallow.fields.Email(required=True)
20
-    display_name = marshmallow.fields.String()
21
-    created = marshmallow.fields.DateTime(format='iso8601')
22
-    is_active = marshmallow.fields.Bool()
24
+    user_id = marshmallow.fields.Int(dump_only=True, example=3)
25
+    email = marshmallow.fields.Email(
26
+        required=True,
27
+        example='suri.cate@algoo.fr'
28
+    )
29
+    display_name = marshmallow.fields.String(
30
+        example='Suri Cate',
31
+    )
32
+    created = marshmallow.fields.DateTime(
33
+        format='iso8601',
34
+        description='User account creation date (iso8601 format).',
35
+    )
36
+    is_active = marshmallow.fields.Bool(
37
+        example=True,
38
+         # TODO - G.M - Explains this value.
39
+    )
23 40
     # TODO - G.M - 17-04-2018 - Restrict timezone values
24
-    timezone = marshmallow.fields.String()
41
+    timezone = marshmallow.fields.String(
42
+        example="Paris/Europe",
43
+    )
25 44
     # TODO - G.M - 17-04-2018 - check this, relative url allowed ?
26 45
     caldav_url = marshmallow.fields.Url(
27 46
         allow_none=True,
28 47
         relative=True,
29
-        attribute='calendar_url'
48
+        attribute='calendar_url',
49
+        example="/api/v2/calendar/user/3.ics/",
50
+        description="The url for calendar CalDAV direct access",
51
+    )
52
+    avatar_url = marshmallow.fields.Url(
53
+        allow_none=True,
54
+        example="/api/v2/assets/avatars/suri-cate.jpg",
55
+        description="avatar_url is the url to the image file. "
56
+                    "If no avatar, then set it to null "
57
+                    "(and frontend will interpret this with a default avatar)",
30 58
     )
31
-    avatar_url = marshmallow.fields.Url(allow_none=True)
32 59
     profile = marshmallow.fields.Nested(
33 60
         ProfileSchema,
34 61
         many=False,
35 62
     )
36 63
 
64
+    class Meta:
65
+        description = 'User account of Tracim'
66
+
37 67
 
38 68
 class UserIdPathSchema(marshmallow.Schema):
39
-    user_id = marshmallow.fields.Int()
69
+    user_id = marshmallow.fields.Int(example=3)
40 70
 
41 71
 
42 72
 class WorkspaceIdPathSchema(marshmallow.Schema):
43
-    workspace_id = marshmallow.fields.Int()
73
+    workspace_id = marshmallow.fields.Int(example=4)
44 74
 
45 75
 
46 76
 class BasicAuthSchema(marshmallow.Schema):
47 77
 
48
-    email = marshmallow.fields.Email(required=True)
49
-    password = marshmallow.fields.String(required=True, load_only=True)
78
+    email = marshmallow.fields.Email(
79
+        example='suri.cate@algoo.fr',
80
+        required=True
81
+    )
82
+    password = marshmallow.fields.String(
83
+        example='8QLa$<w',
84
+        required=True,
85
+        load_only=True,
86
+    )
87
+
88
+    class Meta:
89
+        description = 'Entry for HTTP Basic Auth'
50 90
 
51 91
     @post_load
52 92
     def make_login(self, data):
@@ -58,46 +98,69 @@ class LoginOutputHeaders(marshmallow.Schema):
58 98
 
59 99
 
60 100
 class NoContentSchema(marshmallow.Schema):
101
+
102
+    class Meta:
103
+        description = 'Empty Schema'
61 104
     pass
62 105
 
63 106
 
64 107
 class WorkspaceMenuEntrySchema(marshmallow.Schema):
65
-    slug = marshmallow.fields.String()
66
-    label = marshmallow.fields.String()
67
-    route = marshmallow.fields.String()
68
-    hexcolor = marshmallow.fields.String()
69
-    icon = marshmallow.fields.String()
70
-
71
-
72
-class WorkspaceSchema(marshmallow.Schema):
73
-    id = marshmallow.fields.Int()
74
-    slug = marshmallow.fields.String()
75
-    label = marshmallow.fields.String()
76
-    description = marshmallow.fields.String()
77
-    sidebar_entries = marshmallow.fields.Nested(
78
-        WorkspaceMenuEntrySchema,
79
-        many=True,
108
+    slug = marshmallow.fields.String(example='markdown-pages')
109
+    label = marshmallow.fields.String(example='Markdown Documents')
110
+    route = marshmallow.fields.String(
111
+        example='/#/workspace/{workspace_id}/contents/?type=mardown-page',
112
+        description='the route is the frontend route. '
113
+                    'It may include workspace_id '
114
+                    'which must be replaced on backend size '
115
+                    '(the route must be ready-to-use)'
116
+    )
117
+    icon = marshmallow.fields.String(
118
+        example='file-text-o',
119
+        description='CSS class of the icon. Example: file-o for using Fontawesome file-text-o icon',  # nopep8
120
+    )
121
+    hexcolor = marshmallow.fields.String(
122
+        example='#F0F9DC',
123
+        description='Hexadecimal color of the entry.'
80 124
     )
81 125
 
126
+    class Meta:
127
+        description = 'Entry element of a workspace menu'
128
+
82 129
 
83 130
 class WorkspaceDigestSchema(marshmallow.Schema):
84
-    id = marshmallow.fields.Int()
85
-    label = marshmallow.fields.String()
131
+    id = marshmallow.fields.Int(example=4)
132
+    label = marshmallow.fields.String(example='Intranet')
86 133
     sidebar_entries = marshmallow.fields.Nested(
87 134
         WorkspaceMenuEntrySchema,
88 135
         many=True,
89 136
     )
90 137
 
138
+    class Meta:
139
+        description = 'Digest of workspace informations'
140
+
141
+
142
+class WorkspaceSchema(WorkspaceDigestSchema):
143
+    slug = marshmallow.fields.String(example='intranet')
144
+    description = marshmallow.fields.String(example='All intranet data.')
145
+
146
+    class Meta:
147
+        description = 'Full workspace informations'
148
+
91 149
 
92 150
 class WorkspaceMemberSchema(marshmallow.Schema):
93
-    role_id = marshmallow.fields.Int(validate=OneOf(UserRoleInWorkspace.get_all_role_values()))  # nopep8
94
-    role_slug = marshmallow.fields.String(validate=OneOf(UserRoleInWorkspace.get_all_role_slug()))  # nopep8
95
-    user_id = marshmallow.fields.Int()
96
-    workspace_id = marshmallow.fields.Int()
151
+    role_slug = marshmallow.fields.String(
152
+        example='contributor',
153
+        validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
154
+    )
155
+    user_id = marshmallow.fields.Int(example=3)
156
+    workspace_id = marshmallow.fields.Int(example=4)
97 157
     user = marshmallow.fields.Nested(
98 158
         UserSchema(only=('display_name', 'avatar_url'))
99 159
     )
100 160
 
161
+    class Meta:
162
+        description = 'Workspace Member information'
163
+
101 164
 
102 165
 class ApplicationConfigSchema(marshmallow.Schema):
103 166
     pass
@@ -105,11 +168,23 @@ class ApplicationConfigSchema(marshmallow.Schema):
105 168
 
106 169
 
107 170
 class ApplicationSchema(marshmallow.Schema):
108
-    label = marshmallow.fields.String()
109
-    slug = marshmallow.fields.String()
110
-    icon = marshmallow.fields.String()
111
-    hexcolor = marshmallow.fields.String()
112
-    is_active = marshmallow.fields.Boolean()
171
+    label = marshmallow.fields.String(example='Calendar')
172
+    slug = marshmallow.fields.String(example='calendar')
173
+    icon = marshmallow.fields.String(
174
+        example='file-o',
175
+        description='CSS class of the icon. Example: file-o for using Fontawesome file-o icon',  # nopep8
176
+    )
177
+    hexcolor = marshmallow.fields.String(
178
+        example='#FF0000',
179
+        description='HTML encoded color associated to the application. Example:#FF0000 for red'  # nopep8
180
+    )
181
+    is_active = marshmallow.fields.Boolean(
182
+        example=True,
183
+        description='if true, the application is in use in the context',
184
+    )
113 185
     config = marshmallow.fields.Nested(
114 186
         ApplicationConfigSchema,
115 187
     )
188
+
189
+    class Meta:
190
+        description = 'Tracim Application informations'

+ 10 - 7
tracim/views/core_api/user_controller.py View File

@@ -11,13 +11,14 @@ except ImportError:
11 11
     from http import client as HTTPStatus
12 12
 
13 13
 from tracim import hapic, TracimRequest
14
-from tracim.exceptions import NotAuthentificated, InsufficientUserProfile, \
15
-    UserNotExist
16
-from tracim.lib.core.user import UserApi
14
+
15
+from tracim.exceptions import NotAuthentificated
16
+from tracim.exceptions import InsufficientUserProfile
17
+from tracim.exceptions import UserDoesNotExist
17 18
 from tracim.lib.core.workspace import WorkspaceApi
18 19
 from tracim.views.controllers import Controller
19
-from tracim.views.core_api.schemas import UserIdPathSchema, \
20
-    WorkspaceDigestSchema
20
+from tracim.views.core_api.schemas import UserIdPathSchema
21
+from tracim.views.core_api.schemas import WorkspaceDigestSchema
21 22
 
22 23
 
23 24
 class UserController(Controller):
@@ -25,7 +26,7 @@ class UserController(Controller):
25 26
     @hapic.with_api_doc()
26 27
     @hapic.handle_exception(NotAuthentificated, HTTPStatus.UNAUTHORIZED)
27 28
     @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
28
-    @hapic.handle_exception(UserNotExist, HTTPStatus.NOT_FOUND)
29
+    @hapic.handle_exception(UserDoesNotExist, HTTPStatus.NOT_FOUND)
29 30
     @require_same_user_or_profile(Group.TIM_ADMIN)
30 31
     @hapic.input_path(UserIdPathSchema())
31 32
     @hapic.output_body(WorkspaceDigestSchema(many=True),)
@@ -39,9 +40,11 @@ class UserController(Controller):
39 40
             session=request.dbsession,
40 41
             config=app_config,
41 42
         )
43
+        
44
+        workspaces = wapi.get_all_for_user(request.candidate_user)
42 45
         return [
43 46
             WorkspaceInContext(workspace, request.dbsession, app_config)
44
-            for workspace in wapi.get_all_for_user(request.candidate_user)
47
+            for workspace in workspaces
45 48
         ]
46 49
 
47 50
     def bind(self, configurator: Configurator) -> None:

+ 14 - 10
tracim/views/core_api/workspace_controller.py View File

@@ -5,8 +5,8 @@ from sqlalchemy.orm.exc import NoResultFound
5 5
 
6 6
 from tracim.lib.core.userworkspace import RoleApi
7 7
 from tracim.lib.utils.authorization import require_workspace_role
8
-from tracim.models.context_models import WorkspaceInContext, \
9
-    UserRoleWorkspaceInContext
8
+from tracim.models.context_models import WorkspaceInContext
9
+from tracim.models.context_models import UserRoleWorkspaceInContext
10 10
 from tracim.models.data import UserRoleInWorkspace
11 11
 
12 12
 try:  # Python 3.5+
@@ -15,14 +15,16 @@ except ImportError:
15 15
     from http import client as HTTPStatus
16 16
 
17 17
 from tracim import hapic, TracimRequest
18
-from tracim.exceptions import NotAuthentificated, InsufficientUserProfile, \
19
-    WorkspaceNotFound
18
+from tracim.exceptions import NotAuthentificated
19
+from tracim.exceptions import InsufficientUserProfile
20
+from tracim.exceptions import WorkspaceNotFound
20 21
 from tracim.lib.core.user import UserApi
21 22
 from tracim.lib.core.workspace import WorkspaceApi
22 23
 from tracim.views.controllers import Controller
23
-from tracim.views.core_api.schemas import WorkspaceSchema, UserSchema, \
24
-    WorkspaceIdPathSchema, WorkspaceMemberSchema
25
-
24
+from tracim.views.core_api.schemas import WorkspaceSchema
25
+from tracim.views.core_api.schemas import UserSchema
26
+from tracim.views.core_api.schemas import WorkspaceIdPathSchema
27
+from tracim.views.core_api.schemas import WorkspaceMemberSchema
26 28
 
27 29
 class WorkspaceController(Controller):
28 30
 
@@ -68,15 +70,17 @@ class WorkspaceController(Controller):
68 70
             session=request.dbsession,
69 71
             config=app_config,
70 72
         )
73
+        
74
+        roles = rapi.get_all_for_workspace(request.current_workspace)
71 75
         return [
72 76
             rapi.get_user_role_workspace_with_context(user_role)
73
-            for user_role in rapi.get_all_for_workspace(request.current_workspace)
77
+            for user_role in roles
74 78
         ]
75 79
 
76 80
     def bind(self, configurator: Configurator) -> None:
77 81
         """
78
-        Create all routes and views using pyramid configurator
79
-        for this controller
82
+        Create all routes and views using
83
+        pyramid configurator for this controller
80 84
         """
81 85
 
82 86
         # Applications