Browse Source

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

Guénaël Muller 6 years ago
parent
commit
2c59c563f1

+ 5 - 0
tracim/command/database.py View File

@@ -46,6 +46,10 @@ class InitializeDBCommand(AppContextCommand):
46 46
         config_uri = parsed_args.config_file
47 47
         setup_logging(config_uri)
48 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 53
         self._create_schema(settings)
50 54
         self._populate_database(settings, add_test_data=parsed_args.test_data)
51 55
 
@@ -113,6 +117,7 @@ class DeleteDBCommand(AppContextCommand):
113 117
         config_uri = parsed_args.config_file
114 118
         setup_logging(config_uri)
115 119
         settings = get_appsettings(config_uri)
120
+        settings.update(settings.global_conf)
116 121
         engine = get_engine(settings)
117 122
         app_config = CFG(settings)
118 123
         app_config.configure_filedepot()

+ 1 - 1
tracim/exceptions.py View File

@@ -160,5 +160,5 @@ class EmptyLabelNotAllowed(EmptyValueNotAllowed):
160 160
     pass
161 161
 
162 162
 
163
-class EmptyRawContentNotAllowed(EmptyValueNotAllowed):
163
+class EmptyCommentContentNotAllowed(EmptyValueNotAllowed):
164 164
     pass

+ 22 - 9
tracim/lib/core/content.py View File

@@ -24,7 +24,8 @@ from sqlalchemy.sql.elements import and_
24 24
 
25 25
 from tracim.lib.utils.utils import cmp_to_key
26 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 29
 from tracim.exceptions import EmptyLabelNotAllowed
29 30
 from tracim.exceptions import ContentNotFound
30 31
 from tracim.exceptions import WorkspacesDoNotMatch
@@ -394,20 +395,28 @@ class ContentApi(object):
394 395
         return result
395 396
 
396 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 399
         assert content_type in ContentType.allowed_types()
400
+        assert not (label and filename)
398 401
 
399 402
         if content_type == ContentType.Folder and not label:
400 403
             label = self.generate_folder_label(workspace, parent)
401 404
 
402 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 409
             # set label and file_extension
408 410
             content.file_name = label
411
+        elif label:
412
+            content.label = label
409 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 421
         content.owner = self._user
413 422
         content.parent = parent
@@ -430,11 +439,11 @@ class ContentApi(object):
430 439
     def create_comment(self, workspace: Workspace=None, parent: Content=None, content:str ='', do_save=False) -> Content:
431 440
         assert parent and parent.type != ContentType.Folder
432 441
         if not content:
433
-            raise EmptyRawContentNotAllowed()
442
+            raise EmptyCommentContentNotAllowed()
434 443
         item = Content()
435 444
         item.owner = self._user
436 445
         item.parent = parent
437
-        if parent and not workspace:
446
+        if not workspace:
438 447
             workspace = item.parent.workspace
439 448
         item.workspace = workspace
440 449
         item.type = ContentType.Comment
@@ -446,7 +455,6 @@ class ContentApi(object):
446 455
             self.save(item, ActionDescription.COMMENT)
447 456
         return item
448 457
 
449
-
450 458
     def get_one_from_revision(self, content_id: int, content_type: str, workspace: Workspace=None, revision_id=None) -> Content:
451 459
         """
452 460
         This method is a hack to convert a node revision item into a node
@@ -483,6 +491,11 @@ class ContentApi(object):
483 491
         try:
484 492
             content = base_request.one()
485 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 499
             raise ContentNotFound('Content "{}" not found in database'.format(content_id)) from exc  # nopep8
487 500
         return content
488 501
 

+ 0 - 8
tracim/models/context_models.py View File

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

+ 1 - 0
tracim/tests/functional/test_comments.py View File

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

+ 11 - 6
tracim/tests/library/test_content_api.py View File

@@ -506,7 +506,7 @@ class TestContentApi(DefaultTest):
506 506
             session=self.session,
507 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 510
         with new_revision(
511 511
             session=self.session,
512 512
             tm=transaction.manager,
@@ -546,7 +546,7 @@ class TestContentApi(DefaultTest):
546 546
             session=self.session,
547 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 550
         with new_revision(
551 551
             session=self.session,
552 552
             tm=transaction.manager,
@@ -656,6 +656,7 @@ class TestContentApi(DefaultTest):
656 656
             workspace,
657 657
             None,
658 658
             'folder a',
659
+            '',
659 660
             True
660 661
         )
661 662
         with self.session.no_autoflush:
@@ -692,6 +693,7 @@ class TestContentApi(DefaultTest):
692 693
             workspace2,
693 694
             None,
694 695
             'folder b',
696
+            '',
695 697
             True
696 698
         )
697 699
 
@@ -775,6 +777,7 @@ class TestContentApi(DefaultTest):
775 777
             workspace,
776 778
             None,
777 779
             'folder a',
780
+            '',
778 781
             True
779 782
         )
780 783
         with self.session.no_autoflush:
@@ -811,6 +814,7 @@ class TestContentApi(DefaultTest):
811 814
             workspace2,
812 815
             None,
813 816
             'folder b',
817
+            '',
814 818
             True
815 819
         )
816 820
         api2.copy(
@@ -891,6 +895,7 @@ class TestContentApi(DefaultTest):
891 895
             workspace,
892 896
             None,
893 897
             'folder a',
898
+            '',
894 899
             True
895 900
         )
896 901
         with self.session.no_autoflush:
@@ -2008,9 +2013,9 @@ class TestContentApi(DefaultTest):
2008 2013
             config=self.app_config,
2009 2014
         )
2010 2015
         a = api.create(ContentType.Folder, workspace, None,
2011
-                       'this is randomized folder', True)
2016
+                       'this is randomized folder', '', True)
2012 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 2020
         with new_revision(
2016 2021
             session=self.session,
@@ -2064,9 +2069,9 @@ class TestContentApi(DefaultTest):
2064 2069
             config=self.app_config,
2065 2070
         )
2066 2071
         a = api.create(ContentType.Folder, workspace, None,
2067
-                       'this is randomized folder', True)
2072
+                       'this is randomized folder', '', True)
2068 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 2076
         with new_revision(
2072 2077
             tm=transaction.manager,

+ 2 - 5
tracim/views/contents_api/comment_controller.py View File

@@ -19,10 +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, 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 23
 from tracim.models.contents import ContentTypeLegacy as ContentType
27 24
 from tracim.models.revision_protection import new_revision
28 25
 from tracim.models.data import UserRoleInWorkspace
@@ -59,7 +56,7 @@ class CommentController(Controller):
59 56
         ]
60 57
 
61 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 60
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
64 61
     @hapic.input_path(WorkspaceAndContentIdPathSchema())
65 62
     @hapic.input_body(SetCommentSchema())

+ 70 - 19
tracim/views/core_api/schemas.py View File

@@ -2,6 +2,7 @@
2 2
 import marshmallow
3 3
 from marshmallow import post_load
4 4
 from marshmallow.validate import OneOf
5
+from marshmallow.validate import Range
5 6
 
6 7
 from tracim.lib.utils.utils import DATETIME_FORMAT
7 8
 from tracim.models.auth import Profile
@@ -79,15 +80,30 @@ class UserSchema(UserDigestSchema):
79 80
 
80 81
 
81 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 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 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 109
 class WorkspaceAndContentIdPathSchema(
@@ -102,8 +118,9 @@ class WorkspaceAndContentIdPathSchema(
102 118
 class CommentsPathSchema(WorkspaceAndContentIdPathSchema):
103 119
     comment_id = marshmallow.fields.Int(
104 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 125
     @post_load
109 126
     def make_path_object(self, data):
@@ -118,19 +135,22 @@ class FilterContentQuerySchema(marshmallow.Schema):
118 135
                     ' If not set, then return all contents.'
119 136
                     ' If set to 0, then return root contents.'
120 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 141
     show_archived = marshmallow.fields.Int(
124 142
         example=0,
125 143
         default=0,
126 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 148
     show_deleted = marshmallow.fields.Int(
130 149
         example=0,
131 150
         default=0,
132 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 155
     show_active = marshmallow.fields.Int(
136 156
         example=1,
@@ -140,7 +160,8 @@ class FilterContentQuerySchema(marshmallow.Schema):
140 160
                     ' Note: active content are content '
141 161
                     'that is neither archived nor deleted. '
142 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 166
     content_type = marshmallow.fields.String(
146 167
         example=ContentType.Any,
@@ -209,7 +230,10 @@ class WorkspaceMenuEntrySchema(marshmallow.Schema):
209 230
 
210 231
 
211 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 237
     slug = marshmallow.fields.String(example='intranet')
214 238
     label = marshmallow.fields.String(example='Intranet')
215 239
     sidebar_entries = marshmallow.fields.Nested(
@@ -233,8 +257,14 @@ class WorkspaceMemberSchema(marshmallow.Schema):
233 257
         example='contributor',
234 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 268
     user = marshmallow.fields.Nested(
239 269
         UserSchema(only=('public_name', 'avatar_url'))
240 270
     )
@@ -323,11 +353,13 @@ class ContentMoveSchema(marshmallow.Schema):
323 353
         description='id of the new parent content id.',
324 354
         allow_none=True,
325 355
         required=True,
356
+        validate=Range(min=0, error="Value must be positive or 0"),
326 357
     )
327 358
     new_workspace_id = marshmallow.fields.Int(
328 359
         example=2,
329 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 365
     @post_load
@@ -351,15 +383,20 @@ class ContentCreationSchema(marshmallow.Schema):
351 383
 
352 384
 
353 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 390
     slug = marshmallow.fields.Str(example='intervention-report-12')
356 391
     parent_id = marshmallow.fields.Int(
357 392
         example=34,
358 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 397
     workspace_id = marshmallow.fields.Int(
362 398
         example=19,
399
+        validate=Range(min=1, error="Value must be greater than 0"),
363 400
     )
364 401
     label = marshmallow.fields.Str(example='Intervention Report 12')
365 402
     content_type = marshmallow.fields.Str(
@@ -426,8 +463,16 @@ class TextBasedContentSchema(ContentSchema, TextBasedDataAbstractSchema):
426 463
 
427 464
 
428 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 476
     created = marshmallow.fields.DateTime(
432 477
         format=DATETIME_FORMAT,
433 478
         description='Content creation date',
@@ -440,8 +485,14 @@ class TextBasedRevisionSchema(RevisionSchema, TextBasedDataAbstractSchema):
440 485
 
441 486
 
442 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 496
     raw_content = marshmallow.fields.String(
446 497
         example='<p>This is just an html comment !</p>'
447 498
     )