Przeglądaj źródła

disallow empty raw_content for comment and empty label for others contents + tests

Guénaël Muller 6 lat temu
rodzic
commit
980b8f639b

+ 12 - 0
tracim/exceptions.py Wyświetl plik

@@ -131,3 +131,15 @@ class ContentTypeNotAllowed(TracimException):
131 131
 
132 132
 class WorkspacesDoNotMatch(TracimException):
133 133
     pass
134
+
135
+
136
+class EmptyValueNotAllowed(TracimException):
137
+    pass
138
+
139
+
140
+class EmptyLabelNotAllowed(EmptyValueNotAllowed):
141
+    pass
142
+
143
+
144
+class EmptyRawContentNotAllowed(EmptyValueNotAllowed):
145
+    pass

+ 17 - 6
tracim/lib/core/content.py Wyświetl plik

@@ -5,7 +5,6 @@ import datetime
5 5
 import re
6 6
 import typing
7 7
 from operator import itemgetter
8
-from operator import not_
9 8
 
10 9
 import transaction
11 10
 from sqlalchemy import func
@@ -25,7 +24,8 @@ from sqlalchemy.sql.elements import and_
25 24
 
26 25
 from tracim.lib.utils.utils import cmp_to_key
27 26
 from tracim.lib.core.notifications import NotifierFactory
28
-from tracim.exceptions import SameValueError
27
+from tracim.exceptions import SameValueError, EmptyRawContentNotAllowed
28
+from tracim.exceptions import EmptyLabelNotAllowed
29 29
 from tracim.exceptions import ContentNotFound
30 30
 from tracim.exceptions import WorkspacesDoNotMatch
31 31
 from tracim.lib.utils.utils import current_date_for_filename
@@ -393,18 +393,26 @@ class ContentApi(object):
393 393
 
394 394
         return result
395 395
 
396
-    def create(self, content_type: str, workspace: Workspace, parent: Content=None, label:str ='', do_save=False, is_temporary: bool=False, do_notify=True) -> Content:
396
+    def create(self, content_type: str, workspace: Workspace, parent: Content=None, label: str ='', filename: str = '', do_save=False, is_temporary: bool=False, do_notify=True) -> Content:
397 397
         assert content_type in ContentType.allowed_types()
398 398
 
399 399
         if content_type == ContentType.Folder and not label:
400 400
             label = self.generate_folder_label(workspace, parent)
401 401
 
402 402
         content = Content()
403
+        if label:
404
+            content.label = label
405
+        elif filename:
406
+            # TODO - G.M - 2018-07-04 - File_name setting automatically
407
+            # set label and file_extension
408
+            content.file_name = label
409
+        else:
410
+            raise EmptyLabelNotAllowed()
411
+
403 412
         content.owner = self._user
404 413
         content.parent = parent
405 414
         content.workspace = workspace
406 415
         content.type = content_type
407
-        content.label = label
408 416
         content.is_temporary = is_temporary
409 417
         content.revision_type = ActionDescription.CREATION
410 418
 
@@ -419,9 +427,10 @@ class ContentApi(object):
419 427
             self.save(content, ActionDescription.CREATION, do_notify=do_notify)
420 428
         return content
421 429
 
422
-
423 430
     def create_comment(self, workspace: Workspace=None, parent: Content=None, content:str ='', do_save=False) -> Content:
424
-        assert parent  and parent.type!=ContentType.Folder
431
+        assert parent and parent.type != ContentType.Folder
432
+        if not content:
433
+            raise EmptyRawContentNotAllowed()
425 434
         item = Content()
426 435
         item.owner = self._user
427 436
         item.parent = parent
@@ -972,6 +981,8 @@ class ContentApi(object):
972 981
             # TODO - G.M - 20-03-2018 - Fix internatization for webdav access.
973 982
             # Internatization disabled in libcontent for now.
974 983
             raise SameValueError('The content did not changed')
984
+        if not new_label:
985
+            raise EmptyLabelNotAllowed()
975 986
         item.owner = self._user
976 987
         item.label = new_label
977 988
         item.description = new_content if new_content else item.description # TODO: convert urls into links

+ 1 - 0
tracim/lib/webdav/utils.py Wyświetl plik

@@ -176,6 +176,7 @@ class FakeFileStream(object):
176 176
         is_temporary = self._file_name.startswith('.~') or self._file_name.startswith('~')
177 177
 
178 178
         file = self._api.create(
179
+            filename=self._file_name,
179 180
             content_type=ContentType.File,
180 181
             workspace=self._workspace,
181 182
             parent=self._parent,

+ 19 - 0
tracim/tests/functional/test_comments.py Wyświetl plik

@@ -94,6 +94,25 @@ class TestCommentsEndpoint(FunctionalTest):
94 94
         assert len(res.json_body) == 4
95 95
         assert comment == res.json_body[3]
96 96
 
97
+    def test_api__post_content_comment__err_400__empty_raw_content(self) -> None:
98
+        """
99
+        Get alls comments of a content
100
+        """
101
+        self.testapp.authorization = (
102
+            'Basic',
103
+            (
104
+                'admin@admin.admin',
105
+                'admin@admin.admin'
106
+            )
107
+        )
108
+        params = {
109
+            'raw_content': ''
110
+        }
111
+        res = self.testapp.post_json(
112
+            '/api/v2/workspaces/2/contents/7/comments',
113
+            params=params,
114
+            status=400
115
+        )
97 116
     def test_api__delete_content_comment__ok_200__user_is_owner_and_workspace_manager(self) -> None:  # nopep8
98 117
         """
99 118
         delete comment (user is workspace_manager and owner)

+ 42 - 0
tracim/tests/functional/test_contents.py Wyświetl plik

@@ -192,6 +192,27 @@ class TestHtmlDocuments(FunctionalTest):
192 192
             status=400
193 193
         )
194 194
 
195
+    def test_api__update_html_document__err_400__empty_label(self) -> None:  # nopep8
196
+        """
197
+        Update(put) one html document of a content
198
+        """
199
+        self.testapp.authorization = (
200
+            'Basic',
201
+            (
202
+                'admin@admin.admin',
203
+                'admin@admin.admin'
204
+            )
205
+        )
206
+        params = {
207
+            'label': '',
208
+            'raw_content': '<p> Le nouveau contenu </p>',
209
+        }
210
+        res = self.testapp.put_json(
211
+            '/api/v2/workspaces/2/html-documents/6',
212
+            params=params,
213
+            status=400
214
+        )
215
+
195 216
     def test_api__update_html_document__ok_200__nominal_case(self) -> None:
196 217
         """
197 218
         Update(put) one html document of a content
@@ -603,6 +624,27 @@ class TestThreads(FunctionalTest):
603 624
         assert content['last_modifier'] == content['author']
604 625
         assert content['raw_content'] == '<p> Le nouveau contenu </p>'
605 626
 
627
+    def test_api__update_thread__err_400__empty_label(self) -> None:
628
+        """
629
+        Update(put) thread
630
+        """
631
+        self.testapp.authorization = (
632
+            'Basic',
633
+            (
634
+                'admin@admin.admin',
635
+                'admin@admin.admin'
636
+            )
637
+        )
638
+        params = {
639
+            'label': '',
640
+            'raw_content': '<p> Le nouveau contenu </p>',
641
+        }
642
+        res = self.testapp.put_json(
643
+            '/api/v2/workspaces/2/threads/7',
644
+            params=params,
645
+            status=400
646
+        )
647
+
606 648
     def test_api__get_thread_revisions__ok_200__nominal_case(
607 649
             self
608 650
     ) -> None:

+ 42 - 0
tracim/tests/functional/test_workspaces.py Wyświetl plik

@@ -834,6 +834,48 @@ class TestWorkspaceContents(FunctionalTest):
834 834
         active_contents = self.testapp.get('/api/v2/workspaces/1/contents', params=params_active, status=200).json_body  # nopep8
835 835
         assert res.json_body in active_contents
836 836
 
837
+    def test_api__post_content_create_generic_content__err_400__empty_label(self) -> None:  # nopep8
838
+        """
839
+        Create generic content
840
+        """
841
+        self.testapp.authorization = (
842
+            'Basic',
843
+            (
844
+                'admin@admin.admin',
845
+                'admin@admin.admin'
846
+            )
847
+        )
848
+        params = {
849
+            'label': '',
850
+            'content_type': 'markdownpage',
851
+        }
852
+        res = self.testapp.post_json(
853
+            '/api/v2/workspaces/1/contents',
854
+            params=params,
855
+            status=400
856
+        )
857
+
858
+    def test_api__post_content_create_generic_content__err_400__wrong_content_type(self) -> None:  # nopep8
859
+        """
860
+        Create generic content
861
+        """
862
+        self.testapp.authorization = (
863
+            'Basic',
864
+            (
865
+                'admin@admin.admin',
866
+                'admin@admin.admin'
867
+            )
868
+        )
869
+        params = {
870
+            'label': 'GenericCreatedContent',
871
+            'content_type': 'unexistent-content-type',
872
+        }
873
+        res = self.testapp.post_json(
874
+            '/api/v2/workspaces/1/contents',
875
+            params=params,
876
+            status=400,
877
+        )
878
+
837 879
     def test_api_put_move_content__ok_200__nominal_case(self):
838 880
         """
839 881
         Move content

+ 91 - 24
tracim/tests/library/test_content_api.py Wyświetl plik

@@ -128,10 +128,20 @@ class TestContentApi(DefaultTest):
128 128
             session=self.session,
129 129
             config=self.app_config,
130 130
         )
131
-        item = api.create(ContentType.Folder, workspace, None,
132
-                          'not_deleted', True)
133
-        item2 = api.create(ContentType.Folder, workspace, None,
134
-                           'to_delete', True)
131
+        item = api.create(
132
+            content_type=ContentType.Folder,
133
+            workspace=workspace,
134
+            parent=None,
135
+            label='not_deleted',
136
+            do_save=True
137
+        )
138
+        item2 = api.create(
139
+            content_type=ContentType.Folder,
140
+            workspace=workspace,
141
+            parent=None,
142
+            label='to_delete',
143
+            do_save=True
144
+        )
135 145
         uid = user.user_id
136 146
         wid = workspace.workspace_id
137 147
         transaction.commit()
@@ -229,10 +239,20 @@ class TestContentApi(DefaultTest):
229 239
             session=self.session,
230 240
             config=self.app_config,
231 241
         )
232
-        item = api.create(ContentType.Folder, workspace, None,
233
-                          'not_archived', True)
234
-        item2 = api.create(ContentType.Folder, workspace, None,
235
-                           'to_archive', True)
242
+        item = api.create(
243
+            content_type=ContentType.Folder,
244
+            workspace=workspace,
245
+            parent=None,
246
+            label='not_archived',
247
+            do_save=True
248
+        )
249
+        item2 = api.create(
250
+            content_type=ContentType.Folder,
251
+            workspace=workspace,
252
+            parent=None,
253
+            label='to_archive',
254
+            do_save=True
255
+        )
236 256
         uid = user.user_id
237 257
         wid = workspace.workspace_id
238 258
         transaction.commit()
@@ -337,9 +357,20 @@ class TestContentApi(DefaultTest):
337 357
             session=self.session,
338 358
             config=self.app_config,
339 359
         )
340
-        item = api.create(ContentType.Folder, workspace, None,
341
-                          'thefolder', True)
342
-        item2 = api.create(ContentType.File, workspace, None, 'thefile', True)
360
+        item = api.create(
361
+            content_type=ContentType.Folder,
362
+            workspace=workspace,
363
+            parent=None,
364
+            label='thefolder',
365
+            do_save=True
366
+        )
367
+        item2 = api.create(
368
+            content_type=ContentType.File,
369
+            workspace=workspace,
370
+            parent=None,
371
+            label='thefile',
372
+            do_save=True
373
+        )
343 374
         uid = user.user_id
344 375
         wid = workspace.workspace_id
345 376
         transaction.commit()
@@ -1248,8 +1279,13 @@ class TestContentApi(DefaultTest):
1248 1279
             config=self.app_config,
1249 1280
         )
1250 1281
 
1251
-        p = api.create(ContentType.Page, workspace, None,
1252
-                       'this_is_a_page', True)
1282
+        p = api.create(
1283
+            content_type=ContentType.Page,
1284
+            workspace=workspace,
1285
+            parent=None,
1286
+            label='this_is_a_page',
1287
+            do_save=True
1288
+        )
1253 1289
 
1254 1290
         u1id = user1.user_id
1255 1291
         u2id = user2.user_id
@@ -1453,8 +1489,13 @@ class TestContentApi(DefaultTest):
1453 1489
             session=self.session,
1454 1490
             config=self.app_config,
1455 1491
         )
1456
-        p = api.create(ContentType.File, workspace, None,
1457
-                       'this_is_a_page', True)
1492
+        p = api.create(
1493
+            content_type=ContentType.File,
1494
+            workspace=workspace,
1495
+            parent=None,
1496
+            label='this_is_a_page',
1497
+            do_save=True
1498
+        )
1458 1499
 
1459 1500
         u1id = user1.user_id
1460 1501
         u2id = user2.user_id
@@ -1666,8 +1707,13 @@ class TestContentApi(DefaultTest):
1666 1707
             show_archived=True,
1667 1708
             config=self.app_config,
1668 1709
         )
1669
-        p = api.create(ContentType.File, workspace, None,
1670
-                       'this_is_a_page', True)
1710
+        p = api.create(
1711
+            content_type=ContentType.File,
1712
+            workspace=workspace,
1713
+            parent=None,
1714
+            label='this_is_a_page',
1715
+            do_save=True
1716
+        )
1671 1717
 
1672 1718
         u1id = user1.user_id
1673 1719
         u2id = user2.user_id
@@ -1822,8 +1868,13 @@ class TestContentApi(DefaultTest):
1822 1868
             config=self.app_config,
1823 1869
             show_deleted=True,
1824 1870
         )
1825
-        p = api.create(ContentType.File, workspace, None,
1826
-                       'this_is_a_page', True)
1871
+        p = api.create(
1872
+            content_type=ContentType.File,
1873
+            workspace=workspace,
1874
+            parent=None,
1875
+            label='this_is_a_page',
1876
+            do_save=True
1877
+        )
1827 1878
 
1828 1879
         u1id = user1.user_id
1829 1880
         u2id = user2.user_id
@@ -2065,11 +2116,27 @@ class TestContentApi(DefaultTest):
2065 2116
             session=self.session,
2066 2117
             config=self.app_config,
2067 2118
         )
2068
-        a = api.create(ContentType.Folder, workspace, None,
2069
-                       'this is randomized folder', True)
2070
-        p1 = api.create(ContentType.Page, workspace, a,
2071
-                        'this is dummy label content', True)
2072
-        p2 = api.create(ContentType.Page, workspace, a, 'Hey ! Jon !', True)
2119
+        a = api.create(
2120
+            content_type=ContentType.Folder,
2121
+            workspace=workspace,
2122
+            parent=None,
2123
+            label='this is randomized folder',
2124
+            do_save=True
2125
+        )
2126
+        p1 = api.create(
2127
+            content_type=ContentType.Page,
2128
+            workspace=workspace,
2129
+            parent=a,
2130
+            label='this is dummy label content',
2131
+            do_save=True
2132
+        )
2133
+        p2 = api.create(
2134
+            content_type=ContentType.Page,
2135
+            workspace=workspace,
2136
+            parent=a,
2137
+            label='Hey ! Jon !',
2138
+            do_save=True
2139
+        )
2073 2140
 
2074 2141
         with new_revision(
2075 2142
             session=self.session,

+ 1 - 0
tracim/tests/library/test_webdav.py Wyświetl plik

@@ -95,6 +95,7 @@ class TestWebDav(StandardTest):
95 95
                        self.session,
96 96
                        self.app_config
97 97
                        ).get_one_by_email(email)
98
+
98 99
     def _put_new_text_file(
99 100
             self,
100 101
             provider,

+ 2 - 1
tracim/views/contents_api/comment_controller.py Wyświetl plik

@@ -19,7 +19,7 @@ from tracim.views.core_api.schemas import CommentsPathSchema
19 19
 from tracim.views.core_api.schemas import SetCommentSchema
20 20
 from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
21 21
 from tracim.views.core_api.schemas import NoContentSchema
22
-from tracim.exceptions import WorkspaceNotFound
22
+from tracim.exceptions import WorkspaceNotFound, EmptyRawContentNotAllowed
23 23
 from tracim.exceptions import InsufficientUserRoleInWorkspace
24 24
 from tracim.exceptions import NotAuthenticated
25 25
 from tracim.exceptions import AuthenticationFailed
@@ -59,6 +59,7 @@ class CommentController(Controller):
59 59
         ]
60 60
 
61 61
     @hapic.with_api_doc(tags=[COMMENT_ENDPOINTS_TAG])
62
+    @hapic.handle_exception(EmptyRawContentNotAllowed, HTTPStatus.BAD_REQUEST)
62 63
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
63 64
     @hapic.input_path(WorkspaceAndContentIdPathSchema())
64 65
     @hapic.input_body(SetCommentSchema())

+ 2 - 5
tracim/views/contents_api/html_document_controller.py Wyświetl plik

@@ -23,11 +23,7 @@ from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
23 23
 from tracim.views.core_api.schemas import NoContentSchema
24 24
 from tracim.lib.utils.authorization import require_content_types
25 25
 from tracim.lib.utils.authorization import require_workspace_role
26
-from tracim.exceptions import WorkspaceNotFound
27
-from tracim.exceptions import ContentTypeNotAllowed
28
-from tracim.exceptions import InsufficientUserRoleInWorkspace
29
-from tracim.exceptions import NotAuthenticated
30
-from tracim.exceptions import AuthenticationFailed
26
+from tracim.exceptions import EmptyLabelNotAllowed
31 27
 from tracim.models.context_models import ContentInContext
32 28
 from tracim.models.context_models import RevisionInContext
33 29
 from tracim.models.contents import ContentTypeLegacy as ContentType
@@ -61,6 +57,7 @@ class HTMLDocumentController(Controller):
61 57
         return api.get_content_in_context(content)
62 58
 
63 59
     @hapic.with_api_doc(tags=[HTML_DOCUMENT_ENDPOINTS_TAG])
60
+    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
64 61
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
65 62
     @require_content_types([html_documents_type])
66 63
     @hapic.input_path(WorkspaceAndContentIdPathSchema())

+ 2 - 4
tracim/views/contents_api/threads_controller.py Wyświetl plik

@@ -22,10 +22,7 @@ from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
22 22
 from tracim.views.core_api.schemas import NoContentSchema
23 23
 from tracim.lib.utils.authorization import require_content_types
24 24
 from tracim.lib.utils.authorization import require_workspace_role
25
-from tracim.exceptions import WorkspaceNotFound, ContentTypeNotAllowed
26
-from tracim.exceptions import InsufficientUserRoleInWorkspace
27
-from tracim.exceptions import NotAuthenticated
28
-from tracim.exceptions import AuthenticationFailed
25
+from tracim.exceptions import EmptyLabelNotAllowed
29 26
 from tracim.models.context_models import ContentInContext
30 27
 from tracim.models.context_models import RevisionInContext
31 28
 from tracim.models.contents import ContentTypeLegacy as ContentType
@@ -59,6 +56,7 @@ class ThreadController(Controller):
59 56
         return api.get_content_in_context(content)
60 57
 
61 58
     @hapic.with_api_doc(tags=[THREAD_ENDPOINTS_TAG])
59
+    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
62 60
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
63 61
     @require_content_types([thread_type])
64 62
     @hapic.input_path(WorkspaceAndContentIdPathSchema())

+ 1 - 0
tracim/views/core_api/schemas.py Wyświetl plik

@@ -459,6 +459,7 @@ class SetCommentSchema(marshmallow.Schema):
459 459
 
460 460
 class ContentModifyAbstractSchema(marshmallow.Schema):
461 461
     label = marshmallow.fields.String(
462
+        required=True,
462 463
         example='contract for client XXX',
463 464
         description='New title of the content'
464 465
     )

+ 5 - 6
tracim/views/core_api/workspace_controller.py Wyświetl plik

@@ -11,16 +11,14 @@ from tracim import TracimRequest
11 11
 from tracim.lib.core.workspace import WorkspaceApi
12 12
 from tracim.lib.core.content import ContentApi
13 13
 from tracim.lib.core.userworkspace import RoleApi
14
-from tracim.lib.utils.authorization import require_workspace_role, \
15
-    require_candidate_workspace_role
14
+from tracim.lib.utils.authorization import require_workspace_role
15
+from tracim.lib.utils.authorization import require_candidate_workspace_role
16 16
 from tracim.models.data import UserRoleInWorkspace
17 17
 from tracim.models.data import ActionDescription
18 18
 from tracim.models.context_models import UserRoleWorkspaceInContext
19 19
 from tracim.models.context_models import ContentInContext
20
-from tracim.exceptions import NotAuthenticated, InsufficientUserRoleInWorkspace
21
-from tracim.exceptions import WorkspaceNotFoundInTracimRequest
20
+from tracim.exceptions import EmptyLabelNotAllowed
22 21
 from tracim.exceptions import WorkspacesDoNotMatch
23
-from tracim.exceptions import WorkspaceNotFound
24 22
 from tracim.views.controllers import Controller
25 23
 from tracim.views.core_api.schemas import FilterContentQuerySchema
26 24
 from tracim.views.core_api.schemas import ContentMoveSchema
@@ -117,6 +115,7 @@ class WorkspaceController(Controller):
117 115
 
118 116
     @hapic.with_api_doc(tags=[WORKSPACE_ENDPOINTS_TAG])
119 117
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
118
+    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
120 119
     @hapic.input_path(WorkspaceIdPathSchema())
121 120
     @hapic.input_body(ContentCreationSchema())
122 121
     @hapic.output_body(ContentDigestSchema())
@@ -125,7 +124,7 @@ class WorkspaceController(Controller):
125 124
             context,
126 125
             request: TracimRequest,
127 126
             hapic_data=None,
128
-    ) -> typing.List[ContentInContext]:
127
+    ) -> ContentInContext:
129 128
         """
130 129
         create a generic empty content
131 130
         """