Parcourir la source

Merge branch 'develop' of github.com:tracim/tracim_backend into fix/621_add_worskpace_filter_by_content_type

Guénaël Muller il y a 6 ans
Parent
révision
2c59c563f1

+ 5 - 0
tracim/command/database.py Voir le fichier

46
         config_uri = parsed_args.config_file
46
         config_uri = parsed_args.config_file
47
         setup_logging(config_uri)
47
         setup_logging(config_uri)
48
         settings = get_appsettings(config_uri)
48
         settings = get_appsettings(config_uri)
49
+        # INFO - G.M - 2018-06-178 - We need to add info from [DEFAULT]
50
+        # section of config file in order to have both global and
51
+        # web app specific param.
52
+        settings.update(settings.global_conf)
49
         self._create_schema(settings)
53
         self._create_schema(settings)
50
         self._populate_database(settings, add_test_data=parsed_args.test_data)
54
         self._populate_database(settings, add_test_data=parsed_args.test_data)
51
 
55
 
113
         config_uri = parsed_args.config_file
117
         config_uri = parsed_args.config_file
114
         setup_logging(config_uri)
118
         setup_logging(config_uri)
115
         settings = get_appsettings(config_uri)
119
         settings = get_appsettings(config_uri)
120
+        settings.update(settings.global_conf)
116
         engine = get_engine(settings)
121
         engine = get_engine(settings)
117
         app_config = CFG(settings)
122
         app_config = CFG(settings)
118
         app_config.configure_filedepot()
123
         app_config.configure_filedepot()

+ 1 - 1
tracim/exceptions.py Voir le fichier

160
     pass
160
     pass
161
 
161
 
162
 
162
 
163
-class EmptyRawContentNotAllowed(EmptyValueNotAllowed):
163
+class EmptyCommentContentNotAllowed(EmptyValueNotAllowed):
164
     pass
164
     pass

+ 22 - 9
tracim/lib/core/content.py Voir le fichier

24
 
24
 
25
 from tracim.lib.utils.utils import cmp_to_key
25
 from tracim.lib.utils.utils import cmp_to_key
26
 from tracim.lib.core.notifications import NotifierFactory
26
 from tracim.lib.core.notifications import NotifierFactory
27
-from tracim.exceptions import SameValueError, EmptyRawContentNotAllowed
27
+from tracim.exceptions import SameValueError
28
+from tracim.exceptions import EmptyCommentContentNotAllowed
28
 from tracim.exceptions import EmptyLabelNotAllowed
29
 from tracim.exceptions import EmptyLabelNotAllowed
29
 from tracim.exceptions import ContentNotFound
30
 from tracim.exceptions import ContentNotFound
30
 from tracim.exceptions import WorkspacesDoNotMatch
31
 from tracim.exceptions import WorkspacesDoNotMatch
394
         return result
395
         return result
395
 
396
 
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
     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:
398
+        # TODO - G.M - 2018-07-16 - raise Exception instead of assert
397
         assert content_type in ContentType.allowed_types()
399
         assert content_type in ContentType.allowed_types()
400
+        assert not (label and filename)
398
 
401
 
399
         if content_type == ContentType.Folder and not label:
402
         if content_type == ContentType.Folder and not label:
400
             label = self.generate_folder_label(workspace, parent)
403
             label = self.generate_folder_label(workspace, parent)
401
 
404
 
402
         content = Content()
405
         content = Content()
403
-        if label:
404
-            content.label = label
405
-        elif filename:
406
-            # TODO - G.M - 2018-07-04 - File_name setting automatically
406
+
407
+        if filename:
408
+            # INFO - G.M - 2018-07-04 - File_name setting automatically
407
             # set label and file_extension
409
             # set label and file_extension
408
             content.file_name = label
410
             content.file_name = label
411
+        elif label:
412
+            content.label = label
409
         else:
413
         else:
410
-            raise EmptyLabelNotAllowed()
414
+            if content_type == ContentType.Comment:
415
+                # INFO - G.M - 2018-07-16 - Default label for comments is
416
+                # empty string.
417
+                content.label = ''
418
+            else:
419
+                raise EmptyLabelNotAllowed('Content of this type should have a valid label')  # nopep8
411
 
420
 
412
         content.owner = self._user
421
         content.owner = self._user
413
         content.parent = parent
422
         content.parent = parent
430
     def create_comment(self, workspace: Workspace=None, parent: Content=None, content:str ='', do_save=False) -> Content:
439
     def create_comment(self, workspace: Workspace=None, parent: Content=None, content:str ='', do_save=False) -> Content:
431
         assert parent and parent.type != ContentType.Folder
440
         assert parent and parent.type != ContentType.Folder
432
         if not content:
441
         if not content:
433
-            raise EmptyRawContentNotAllowed()
442
+            raise EmptyCommentContentNotAllowed()
434
         item = Content()
443
         item = Content()
435
         item.owner = self._user
444
         item.owner = self._user
436
         item.parent = parent
445
         item.parent = parent
437
-        if parent and not workspace:
446
+        if not workspace:
438
             workspace = item.parent.workspace
447
             workspace = item.parent.workspace
439
         item.workspace = workspace
448
         item.workspace = workspace
440
         item.type = ContentType.Comment
449
         item.type = ContentType.Comment
446
             self.save(item, ActionDescription.COMMENT)
455
             self.save(item, ActionDescription.COMMENT)
447
         return item
456
         return item
448
 
457
 
449
-
450
     def get_one_from_revision(self, content_id: int, content_type: str, workspace: Workspace=None, revision_id=None) -> Content:
458
     def get_one_from_revision(self, content_id: int, content_type: str, workspace: Workspace=None, revision_id=None) -> Content:
451
         """
459
         """
452
         This method is a hack to convert a node revision item into a node
460
         This method is a hack to convert a node revision item into a node
483
         try:
491
         try:
484
             content = base_request.one()
492
             content = base_request.one()
485
         except NoResultFound as exc:
493
         except NoResultFound as exc:
494
+            # TODO - G.M - 2018-07-16 - Add better support for all different
495
+            # error case who can happened here
496
+            # like content doesn't exist, wrong parent, wrong content_type, wrong workspace,
497
+            # wrong access to this workspace, wrong base filter according
498
+            # to content_status.
486
             raise ContentNotFound('Content "{}" not found in database'.format(content_id)) from exc  # nopep8
499
             raise ContentNotFound('Content "{}" not found in database'.format(content_id)) from exc  # nopep8
487
         return content
500
         return content
488
 
501
 

+ 0 - 8
tracim/models/context_models.py Voir le fichier

340
         return self.content.content_id
340
         return self.content.content_id
341
 
341
 
342
     @property
342
     @property
343
-    def id(self) -> int:
344
-        return self.content_id
345
-
346
-    @property
347
     def parent_id(self) -> int:
343
     def parent_id(self) -> int:
348
         """
344
         """
349
         Return parent_id of the content
345
         Return parent_id of the content
447
         return self.revision.content_id
443
         return self.revision.content_id
448
 
444
 
449
     @property
445
     @property
450
-    def id(self) -> int:
451
-        return self.content_id
452
-
453
-    @property
454
     def parent_id(self) -> int:
446
     def parent_id(self) -> int:
455
         """
447
         """
456
         Return parent_id of the content
448
         Return parent_id of the content

+ 1 - 0
tracim/tests/functional/test_comments.py Voir le fichier

113
             params=params,
113
             params=params,
114
             status=400
114
             status=400
115
         )
115
         )
116
+
116
     def test_api__delete_content_comment__ok_200__user_is_owner_and_workspace_manager(self) -> None:  # nopep8
117
     def test_api__delete_content_comment__ok_200__user_is_owner_and_workspace_manager(self) -> None:  # nopep8
117
         """
118
         """
118
         delete comment (user is workspace_manager and owner)
119
         delete comment (user is workspace_manager and owner)

+ 11 - 6
tracim/tests/library/test_content_api.py Voir le fichier

506
             session=self.session,
506
             session=self.session,
507
             config=self.app_config,
507
             config=self.app_config,
508
         )
508
         )
509
-        c = api.create(ContentType.Folder, workspace, None, 'parent', True)
509
+        c = api.create(ContentType.Folder, workspace, None, 'parent', '', True)
510
         with new_revision(
510
         with new_revision(
511
             session=self.session,
511
             session=self.session,
512
             tm=transaction.manager,
512
             tm=transaction.manager,
546
             session=self.session,
546
             session=self.session,
547
             config=self.app_config,
547
             config=self.app_config,
548
         )
548
         )
549
-        c = api.create(ContentType.Folder, workspace, None, 'parent', True)
549
+        c = api.create(ContentType.Folder, workspace, None, 'parent', '', True)
550
         with new_revision(
550
         with new_revision(
551
             session=self.session,
551
             session=self.session,
552
             tm=transaction.manager,
552
             tm=transaction.manager,
656
             workspace,
656
             workspace,
657
             None,
657
             None,
658
             'folder a',
658
             'folder a',
659
+            '',
659
             True
660
             True
660
         )
661
         )
661
         with self.session.no_autoflush:
662
         with self.session.no_autoflush:
692
             workspace2,
693
             workspace2,
693
             None,
694
             None,
694
             'folder b',
695
             'folder b',
696
+            '',
695
             True
697
             True
696
         )
698
         )
697
 
699
 
775
             workspace,
777
             workspace,
776
             None,
778
             None,
777
             'folder a',
779
             'folder a',
780
+            '',
778
             True
781
             True
779
         )
782
         )
780
         with self.session.no_autoflush:
783
         with self.session.no_autoflush:
811
             workspace2,
814
             workspace2,
812
             None,
815
             None,
813
             'folder b',
816
             'folder b',
817
+            '',
814
             True
818
             True
815
         )
819
         )
816
         api2.copy(
820
         api2.copy(
891
             workspace,
895
             workspace,
892
             None,
896
             None,
893
             'folder a',
897
             'folder a',
898
+            '',
894
             True
899
             True
895
         )
900
         )
896
         with self.session.no_autoflush:
901
         with self.session.no_autoflush:
2008
             config=self.app_config,
2013
             config=self.app_config,
2009
         )
2014
         )
2010
         a = api.create(ContentType.Folder, workspace, None,
2015
         a = api.create(ContentType.Folder, workspace, None,
2011
-                       'this is randomized folder', True)
2016
+                       'this is randomized folder', '', True)
2012
         p = api.create(ContentType.Page, workspace, a,
2017
         p = api.create(ContentType.Page, workspace, a,
2013
-                       'this is randomized label content', True)
2018
+                       'this is randomized label content', '', True)
2014
 
2019
 
2015
         with new_revision(
2020
         with new_revision(
2016
             session=self.session,
2021
             session=self.session,
2064
             config=self.app_config,
2069
             config=self.app_config,
2065
         )
2070
         )
2066
         a = api.create(ContentType.Folder, workspace, None,
2071
         a = api.create(ContentType.Folder, workspace, None,
2067
-                       'this is randomized folder', True)
2072
+                       'this is randomized folder', '', True)
2068
         p = api.create(ContentType.Page, workspace, a,
2073
         p = api.create(ContentType.Page, workspace, a,
2069
-                       'this is dummy label content', True)
2074
+                       'this is dummy label content', '', True)
2070
 
2075
 
2071
         with new_revision(
2076
         with new_revision(
2072
             tm=transaction.manager,
2077
             tm=transaction.manager,

+ 2 - 5
tracim/views/contents_api/comment_controller.py Voir le fichier

19
 from tracim.views.core_api.schemas import SetCommentSchema
19
 from tracim.views.core_api.schemas import SetCommentSchema
20
 from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
20
 from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
21
 from tracim.views.core_api.schemas import NoContentSchema
21
 from tracim.views.core_api.schemas import NoContentSchema
22
-from tracim.exceptions import WorkspaceNotFound, EmptyRawContentNotAllowed
23
-from tracim.exceptions import InsufficientUserRoleInWorkspace
24
-from tracim.exceptions import NotAuthenticated
25
-from tracim.exceptions import AuthenticationFailed
22
+from tracim.exceptions import EmptyCommentContentNotAllowed
26
 from tracim.models.contents import ContentTypeLegacy as ContentType
23
 from tracim.models.contents import ContentTypeLegacy as ContentType
27
 from tracim.models.revision_protection import new_revision
24
 from tracim.models.revision_protection import new_revision
28
 from tracim.models.data import UserRoleInWorkspace
25
 from tracim.models.data import UserRoleInWorkspace
59
         ]
56
         ]
60
 
57
 
61
     @hapic.with_api_doc(tags=[COMMENT_ENDPOINTS_TAG])
58
     @hapic.with_api_doc(tags=[COMMENT_ENDPOINTS_TAG])
62
-    @hapic.handle_exception(EmptyRawContentNotAllowed, HTTPStatus.BAD_REQUEST)
59
+    @hapic.handle_exception(EmptyCommentContentNotAllowed, HTTPStatus.BAD_REQUEST)  # nopep8
63
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
60
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
64
     @hapic.input_path(WorkspaceAndContentIdPathSchema())
61
     @hapic.input_path(WorkspaceAndContentIdPathSchema())
65
     @hapic.input_body(SetCommentSchema())
62
     @hapic.input_body(SetCommentSchema())

+ 70 - 19
tracim/views/core_api/schemas.py Voir le fichier

2
 import marshmallow
2
 import marshmallow
3
 from marshmallow import post_load
3
 from marshmallow import post_load
4
 from marshmallow.validate import OneOf
4
 from marshmallow.validate import OneOf
5
+from marshmallow.validate import Range
5
 
6
 
6
 from tracim.lib.utils.utils import DATETIME_FORMAT
7
 from tracim.lib.utils.utils import DATETIME_FORMAT
7
 from tracim.models.auth import Profile
8
 from tracim.models.auth import Profile
79
 
80
 
80
 
81
 
81
 class UserIdPathSchema(marshmallow.Schema):
82
 class UserIdPathSchema(marshmallow.Schema):
82
-    user_id = marshmallow.fields.Int(example=3, required=True)
83
+    user_id = marshmallow.fields.Int(
84
+        example=3,
85
+        required=True,
86
+        description='id of a valid user',
87
+        validate=Range(min=1, error="Value must be greater than 0"),
88
+    )
83
 
89
 
84
 
90
 
85
 class WorkspaceIdPathSchema(marshmallow.Schema):
91
 class WorkspaceIdPathSchema(marshmallow.Schema):
86
-    workspace_id = marshmallow.fields.Int(example=4, required=True)
92
+    workspace_id = marshmallow.fields.Int(
93
+        example=4,
94
+        required=True,
95
+        description='id of a valid workspace',
96
+        validate=Range(min=1, error="Value must be greater than 0"),
97
+    )
87
 
98
 
88
 
99
 
89
 class ContentIdPathSchema(marshmallow.Schema):
100
 class ContentIdPathSchema(marshmallow.Schema):
90
-    content_id = marshmallow.fields.Int(example=6, required=True)
101
+    content_id = marshmallow.fields.Int(
102
+        example=6,
103
+        required=True,
104
+        description='id of a valid content',
105
+        validate=Range(min=1, error="Value must be greater than 0"),
106
+    )
91
 
107
 
92
 
108
 
93
 class WorkspaceAndContentIdPathSchema(
109
 class WorkspaceAndContentIdPathSchema(
102
 class CommentsPathSchema(WorkspaceAndContentIdPathSchema):
118
 class CommentsPathSchema(WorkspaceAndContentIdPathSchema):
103
     comment_id = marshmallow.fields.Int(
119
     comment_id = marshmallow.fields.Int(
104
         example=6,
120
         example=6,
105
-        description='id of a comment related to content content_id',
106
-        required=True
121
+        description='id of a valid comment related to content content_id',
122
+        required=True,
123
+        validate=Range(min=1, error="Value must be greater than 0"),
107
     )
124
     )
108
     @post_load
125
     @post_load
109
     def make_path_object(self, data):
126
     def make_path_object(self, data):
118
                     ' If not set, then return all contents.'
135
                     ' If not set, then return all contents.'
119
                     ' If set to 0, then return root contents.'
136
                     ' If set to 0, then return root contents.'
120
                     ' If set to another value, return all contents'
137
                     ' If set to another value, return all contents'
121
-                    ' directly included in the folder parent_id'
138
+                    ' directly included in the folder parent_id',
139
+        validate=Range(min=0, error="Value must be positive or 0"),
122
     )
140
     )
123
     show_archived = marshmallow.fields.Int(
141
     show_archived = marshmallow.fields.Int(
124
         example=0,
142
         example=0,
125
         default=0,
143
         default=0,
126
         description='if set to 1, then show archived contents.'
144
         description='if set to 1, then show archived contents.'
127
-                    ' Default is 0 - hide archived content'
145
+                    ' Default is 0 - hide archived content',
146
+        validate=Range(min=0, max=1, error="Value must be 0 or 1"),
128
     )
147
     )
129
     show_deleted = marshmallow.fields.Int(
148
     show_deleted = marshmallow.fields.Int(
130
         example=0,
149
         example=0,
131
         default=0,
150
         default=0,
132
         description='if set to 1, then show deleted contents.'
151
         description='if set to 1, then show deleted contents.'
133
-                    ' Default is 0 - hide deleted content'
152
+                    ' Default is 0 - hide deleted content',
153
+        validate=Range(min=0, max=1, error="Value must be 0 or 1"),
134
     )
154
     )
135
     show_active = marshmallow.fields.Int(
155
     show_active = marshmallow.fields.Int(
136
         example=1,
156
         example=1,
140
                     ' Note: active content are content '
160
                     ' Note: active content are content '
141
                     'that is neither archived nor deleted. '
161
                     'that is neither archived nor deleted. '
142
                     'The reason for this parameter to exist is for example '
162
                     'The reason for this parameter to exist is for example '
143
-                    'to allow to show only archived documents'
163
+                    'to allow to show only archived documents',
164
+        validate=Range(min=0, max=1, error="Value must be 0 or 1"),
144
     )
165
     )
145
     content_type = marshmallow.fields.String(
166
     content_type = marshmallow.fields.String(
146
         example=ContentType.Any,
167
         example=ContentType.Any,
209
 
230
 
210
 
231
 
211
 class WorkspaceDigestSchema(marshmallow.Schema):
232
 class WorkspaceDigestSchema(marshmallow.Schema):
212
-    workspace_id = marshmallow.fields.Int(example=4)
233
+    workspace_id = marshmallow.fields.Int(
234
+        example=4,
235
+        validate=Range(min=1, error="Value must be greater than 0"),
236
+    )
213
     slug = marshmallow.fields.String(example='intranet')
237
     slug = marshmallow.fields.String(example='intranet')
214
     label = marshmallow.fields.String(example='Intranet')
238
     label = marshmallow.fields.String(example='Intranet')
215
     sidebar_entries = marshmallow.fields.Nested(
239
     sidebar_entries = marshmallow.fields.Nested(
233
         example='contributor',
257
         example='contributor',
234
         validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
258
         validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
235
     )
259
     )
236
-    user_id = marshmallow.fields.Int(example=3)
237
-    workspace_id = marshmallow.fields.Int(example=4)
260
+    user_id = marshmallow.fields.Int(
261
+        example=3,
262
+        validate=Range(min=1, error="Value must be greater than 0"),
263
+    )
264
+    workspace_id = marshmallow.fields.Int(
265
+        example=4,
266
+        validate=Range(min=1, error="Value must be greater than 0"),
267
+    )
238
     user = marshmallow.fields.Nested(
268
     user = marshmallow.fields.Nested(
239
         UserSchema(only=('public_name', 'avatar_url'))
269
         UserSchema(only=('public_name', 'avatar_url'))
240
     )
270
     )
323
         description='id of the new parent content id.',
353
         description='id of the new parent content id.',
324
         allow_none=True,
354
         allow_none=True,
325
         required=True,
355
         required=True,
356
+        validate=Range(min=0, error="Value must be positive or 0"),
326
     )
357
     )
327
     new_workspace_id = marshmallow.fields.Int(
358
     new_workspace_id = marshmallow.fields.Int(
328
         example=2,
359
         example=2,
329
         description='id of the new workspace id.',
360
         description='id of the new workspace id.',
330
-        required=True
361
+        required=True,
362
+        validate=Range(min=1, error="Value must be greater than 0"),
331
     )
363
     )
332
 
364
 
333
     @post_load
365
     @post_load
351
 
383
 
352
 
384
 
353
 class ContentDigestSchema(marshmallow.Schema):
385
 class ContentDigestSchema(marshmallow.Schema):
354
-    content_id = marshmallow.fields.Int(example=6)
386
+    content_id = marshmallow.fields.Int(
387
+        example=6,
388
+        validate=Range(min=1, error="Value must be greater than 0"),
389
+    )
355
     slug = marshmallow.fields.Str(example='intervention-report-12')
390
     slug = marshmallow.fields.Str(example='intervention-report-12')
356
     parent_id = marshmallow.fields.Int(
391
     parent_id = marshmallow.fields.Int(
357
         example=34,
392
         example=34,
358
         allow_none=True,
393
         allow_none=True,
359
-        default=None
394
+        default=None,
395
+        validate=Range(min=0, error="Value must be positive or 0"),
360
     )
396
     )
361
     workspace_id = marshmallow.fields.Int(
397
     workspace_id = marshmallow.fields.Int(
362
         example=19,
398
         example=19,
399
+        validate=Range(min=1, error="Value must be greater than 0"),
363
     )
400
     )
364
     label = marshmallow.fields.Str(example='Intervention Report 12')
401
     label = marshmallow.fields.Str(example='Intervention Report 12')
365
     content_type = marshmallow.fields.Str(
402
     content_type = marshmallow.fields.Str(
426
 
463
 
427
 
464
 
428
 class RevisionSchema(ContentDigestSchema):
465
 class RevisionSchema(ContentDigestSchema):
429
-    comment_ids = marshmallow.fields.List(marshmallow.fields.Int(example=4))
430
-    revision_id = marshmallow.fields.Int(example=12)
466
+    comment_ids = marshmallow.fields.List(
467
+        marshmallow.fields.Int(
468
+            example=4,
469
+            validate=Range(min=1, error="Value must be greater than 0"),
470
+        )
471
+    )
472
+    revision_id = marshmallow.fields.Int(
473
+        example=12,
474
+        validate=Range(min=1, error="Value must be greater than 0"),
475
+    )
431
     created = marshmallow.fields.DateTime(
476
     created = marshmallow.fields.DateTime(
432
         format=DATETIME_FORMAT,
477
         format=DATETIME_FORMAT,
433
         description='Content creation date',
478
         description='Content creation date',
440
 
485
 
441
 
486
 
442
 class CommentSchema(marshmallow.Schema):
487
 class CommentSchema(marshmallow.Schema):
443
-    content_id = marshmallow.fields.Int(example=6)
444
-    parent_id = marshmallow.fields.Int(example=34)
488
+    content_id = marshmallow.fields.Int(
489
+        example=6,
490
+        validate=Range(min=1, error="Value must be greater than 0"),
491
+    )
492
+    parent_id = marshmallow.fields.Int(
493
+        example=34,
494
+        validate=Range(min=0, error="Value must be positive or 0"),
495
+    )
445
     raw_content = marshmallow.fields.String(
496
     raw_content = marshmallow.fields.String(
446
         example='<p>This is just an html comment !</p>'
497
         example='<p>This is just an html comment !</p>'
447
     )
498
     )