Selaa lähdekoodia

Merge branch 'develop' of github.com:tracim/tracim_backend into feature/611_workspace_and_workspace_member_action_endpoints

Guénaël Muller 6 vuotta sitten
vanhempi
commit
8871ca51d4

+ 2 - 0
tracim/__init__.py Näytä tiedosto

29
 from tracim.views.contents_api.comment_controller import CommentController
29
 from tracim.views.contents_api.comment_controller import CommentController
30
 from tracim.views.errors import ErrorSchema
30
 from tracim.views.errors import ErrorSchema
31
 from tracim.exceptions import NotAuthenticated
31
 from tracim.exceptions import NotAuthenticated
32
+from tracim.exceptions import InvalidId
32
 from tracim.exceptions import InsufficientUserProfile
33
 from tracim.exceptions import InsufficientUserProfile
33
 from tracim.exceptions import InsufficientUserRoleInWorkspace
34
 from tracim.exceptions import InsufficientUserRoleInWorkspace
34
 from tracim.exceptions import WorkspaceNotFoundInTracimRequest
35
 from tracim.exceptions import WorkspaceNotFoundInTracimRequest
90
     context.handle_exception(UserDoesNotExist, HTTPStatus.BAD_REQUEST)
91
     context.handle_exception(UserDoesNotExist, HTTPStatus.BAD_REQUEST)
91
     context.handle_exception(ContentNotFound, HTTPStatus.BAD_REQUEST)
92
     context.handle_exception(ContentNotFound, HTTPStatus.BAD_REQUEST)
92
     context.handle_exception(ContentTypeNotAllowed, HTTPStatus.BAD_REQUEST)
93
     context.handle_exception(ContentTypeNotAllowed, HTTPStatus.BAD_REQUEST)
94
+    context.handle_exception(InvalidId, HTTPStatus.BAD_REQUEST)
93
     # Auth exception
95
     # Auth exception
94
     context.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
96
     context.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
95
     context.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN)
97
     context.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN)

+ 5 - 0
tracim/command/database.py Näytä tiedosto

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()

+ 20 - 1
tracim/exceptions.py Näytä tiedosto

121
     pass
121
     pass
122
 
122
 
123
 
123
 
124
+class InvalidId(TracimException):
125
+    pass
126
+
127
+
128
+class InvalidContentId(InvalidId):
129
+    pass
130
+
131
+
132
+class InvalidCommentId(InvalidId):
133
+    pass
134
+
135
+
136
+class InvalidWorkspaceId(InvalidId):
137
+    pass
138
+
139
+
140
+class InvalidUserId(InvalidId):
141
+    pass
142
+
124
 class ContentNotFound(TracimException):
143
 class ContentNotFound(TracimException):
125
     pass
144
     pass
126
 
145
 
141
     pass
160
     pass
142
 
161
 
143
 
162
 
144
-class EmptyRawContentNotAllowed(EmptyValueNotAllowed):
163
+class EmptyCommentContentNotAllowed(EmptyValueNotAllowed):
145
     pass
164
     pass
146
 
165
 
147
 
166
 

+ 22 - 9
tracim/lib/core/content.py Näytä tiedosto

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
 

+ 19 - 10
tracim/lib/utils/request.py Näytä tiedosto

2
 from pyramid.request import Request
2
 from pyramid.request import Request
3
 from sqlalchemy.orm.exc import NoResultFound
3
 from sqlalchemy.orm.exc import NoResultFound
4
 
4
 
5
-from tracim.exceptions import NotAuthenticated, ContentNotFound
5
+from tracim.exceptions import NotAuthenticated
6
+from tracim.exceptions import ContentNotFound
7
+from tracim.exceptions import InvalidUserId
8
+from tracim.exceptions import InvalidWorkspaceId
9
+from tracim.exceptions import InvalidContentId
10
+from tracim.exceptions import InvalidCommentId
6
 from tracim.exceptions import ContentNotFoundInTracimRequest
11
 from tracim.exceptions import ContentNotFoundInTracimRequest
7
 from tracim.exceptions import WorkspaceNotFoundInTracimRequest
12
 from tracim.exceptions import WorkspaceNotFoundInTracimRequest
8
 from tracim.exceptions import UserNotFoundInTracimRequest
13
 from tracim.exceptions import UserNotFoundInTracimRequest
214
         comment_id = ''
219
         comment_id = ''
215
         try:
220
         try:
216
             if 'comment_id' in request.matchdict:
221
             if 'comment_id' in request.matchdict:
217
-                if not request.matchdict['comment_id'].isdecimal():
218
-                    raise ContentNotFoundInTracimRequest('comment_id is not a correct integer')  # nopep8
222
+                comment_id_str = request.matchdict['content_id']
223
+                if not isinstance(comment_id_str, str) or not comment_id_str.isdecimal():  # nopep8
224
+                    raise InvalidCommentId('comment_id is not a correct integer')  # nopep8
219
                 comment_id = int(request.matchdict['comment_id'])
225
                 comment_id = int(request.matchdict['comment_id'])
220
             if not comment_id:
226
             if not comment_id:
221
                 raise ContentNotFoundInTracimRequest('No comment_id property found in request')  # nopep8
227
                 raise ContentNotFoundInTracimRequest('No comment_id property found in request')  # nopep8
253
         content_id = ''
259
         content_id = ''
254
         try:
260
         try:
255
             if 'content_id' in request.matchdict:
261
             if 'content_id' in request.matchdict:
256
-                if not request.matchdict['content_id'].isdecimal():
257
-                    raise ContentNotFoundInTracimRequest('content_id is not a correct integer')  # nopep8
262
+                content_id_str = request.matchdict['content_id']
263
+                if not isinstance(content_id_str, str) or not content_id_str.isdecimal():  # nopep8
264
+                    raise InvalidContentId('content_id is not a correct integer')  # nopep8
258
                 content_id = int(request.matchdict['content_id'])
265
                 content_id = int(request.matchdict['content_id'])
259
             if not content_id:
266
             if not content_id:
260
                 raise ContentNotFoundInTracimRequest('No content_id property found in request')  # nopep8
267
                 raise ContentNotFoundInTracimRequest('No content_id property found in request')  # nopep8
286
         try:
293
         try:
287
             login = None
294
             login = None
288
             if 'user_id' in request.matchdict:
295
             if 'user_id' in request.matchdict:
289
-                if not request.matchdict['user_id'].isdecimal():
290
-                    raise UserNotFoundInTracimRequest('user_id is not a correct integer')  # nopep8
296
+                user_id_str = request.matchdict['user_id']
297
+                if not isinstance(user_id_str, str) or not user_id_str.isdecimal():
298
+                    raise InvalidUserId('user_id is not a correct integer')  # nopep8
291
                 login = int(request.matchdict['user_id'])
299
                 login = int(request.matchdict['user_id'])
292
             if not login:
300
             if not login:
293
                 raise UserNotFoundInTracimRequest('You request a candidate user but the context not permit to found one')  # nopep8
301
                 raise UserNotFoundInTracimRequest('You request a candidate user but the context not permit to found one')  # nopep8
331
         workspace_id = ''
339
         workspace_id = ''
332
         try:
340
         try:
333
             if 'workspace_id' in request.matchdict:
341
             if 'workspace_id' in request.matchdict:
334
-                if not request.matchdict['workspace_id'].isdecimal():
335
-                    raise WorkspaceNotFoundInTracimRequest('workspace_id is not a correct integer')  # nopep8
342
+                workspace_id_str = request.matchdict['workspace_id']
343
+                if not isinstance(workspace_id_str, str) or not workspace_id_str.isdecimal():  # nopep8
344
+                    raise InvalidWorkspaceId('workspace_id is not a correct integer')  # nopep8
336
                 workspace_id = int(request.matchdict['workspace_id'])
345
                 workspace_id = int(request.matchdict['workspace_id'])
337
             if not workspace_id:
346
             if not workspace_id:
338
                 raise WorkspaceNotFoundInTracimRequest('No workspace_id property found in request')  # nopep8
347
                 raise WorkspaceNotFoundInTracimRequest('No workspace_id property found in request')  # nopep8
368
                     if workspace_id.isdecimal():
377
                     if workspace_id.isdecimal():
369
                         workspace_id = int(workspace_id)
378
                         workspace_id = int(workspace_id)
370
                     else:
379
                     else:
371
-                        raise WorkspaceNotFoundInTracimRequest('workspace_id is not a correct integer')  # nopep8
380
+                        raise InvalidWorkspaceId('workspace_id is not a correct integer')  # nopep8
372
             if not workspace_id:
381
             if not workspace_id:
373
                 raise WorkspaceNotFoundInTracimRequest('No new_workspace_id property found in body')  # nopep8
382
                 raise WorkspaceNotFoundInTracimRequest('No new_workspace_id property found in body')  # nopep8
374
             wapi = WorkspaceApi(
383
             wapi = WorkspaceApi(

+ 0 - 8
tracim/models/context_models.py Näytä tiedosto

406
         return self.content.content_id
406
         return self.content.content_id
407
 
407
 
408
     @property
408
     @property
409
-    def id(self) -> int:
410
-        return self.content_id
411
-
412
-    @property
413
     def parent_id(self) -> int:
409
     def parent_id(self) -> int:
414
         """
410
         """
415
         Return parent_id of the content
411
         Return parent_id of the content
513
         return self.revision.content_id
509
         return self.revision.content_id
514
 
510
 
515
     @property
511
     @property
516
-    def id(self) -> int:
517
-        return self.content_id
518
-
519
-    @property
520
     def parent_id(self) -> int:
512
     def parent_id(self) -> int:
521
         """
513
         """
522
         Return parent_id of the content
514
         Return parent_id of the content

+ 1 - 0
tracim/tests/functional/test_comments.py Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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 Näytä tiedosto

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
83
 
84
 
84
 
85
 
85
 class UserIdPathSchema(marshmallow.Schema):
86
 class UserIdPathSchema(marshmallow.Schema):
86
-    user_id = marshmallow.fields.Int(example=3, required=True)
87
+    user_id = marshmallow.fields.Int(
88
+        example=3,
89
+        required=True,
90
+        description='id of a valid user',
91
+        validate=Range(min=1, error="Value must be greater than 0"),
92
+    )
87
 
93
 
88
 
94
 
89
 class WorkspaceIdPathSchema(marshmallow.Schema):
95
 class WorkspaceIdPathSchema(marshmallow.Schema):
90
-    workspace_id = marshmallow.fields.Int(example=4, required=True)
96
+    workspace_id = marshmallow.fields.Int(
97
+        example=4,
98
+        required=True,
99
+        description='id of a valid workspace',
100
+        validate=Range(min=1, error="Value must be greater than 0"),
101
+    )
91
 
102
 
92
 
103
 
93
 class ContentIdPathSchema(marshmallow.Schema):
104
 class ContentIdPathSchema(marshmallow.Schema):
94
-    content_id = marshmallow.fields.Int(example=6, required=True)
105
+    content_id = marshmallow.fields.Int(
106
+        example=6,
107
+        required=True,
108
+        description='id of a valid content',
109
+        validate=Range(min=1, error="Value must be greater than 0"),
110
+    )
95
 
111
 
96
 
112
 
97
 class WorkspaceAndUserIdPathSchema(
113
 class WorkspaceAndUserIdPathSchema(
115
 class CommentsPathSchema(WorkspaceAndContentIdPathSchema):
131
 class CommentsPathSchema(WorkspaceAndContentIdPathSchema):
116
     comment_id = marshmallow.fields.Int(
132
     comment_id = marshmallow.fields.Int(
117
         example=6,
133
         example=6,
118
-        description='id of a comment related to content content_id',
119
-        required=True
134
+        description='id of a valid comment related to content content_id',
135
+        required=True,
136
+        validate=Range(min=1, error="Value must be greater than 0"),
120
     )
137
     )
121
 
138
 
122
     @post_load
139
     @post_load
132
                     ' If not set, then return all contents.'
149
                     ' If not set, then return all contents.'
133
                     ' If set to 0, then return root contents.'
150
                     ' If set to 0, then return root contents.'
134
                     ' If set to another value, return all contents'
151
                     ' If set to another value, return all contents'
135
-                    ' directly included in the folder parent_id'
152
+                    ' directly included in the folder parent_id',
153
+        validate=Range(min=0, error="Value must be positive or 0"),
136
     )
154
     )
137
     show_archived = marshmallow.fields.Int(
155
     show_archived = marshmallow.fields.Int(
138
         example=0,
156
         example=0,
139
         default=0,
157
         default=0,
140
         description='if set to 1, then show archived contents.'
158
         description='if set to 1, then show archived contents.'
141
-                    ' Default is 0 - hide archived content'
159
+                    ' Default is 0 - hide archived content',
160
+        validate=Range(min=0, max=1, error="Value must be 0 or 1"),
142
     )
161
     )
143
     show_deleted = marshmallow.fields.Int(
162
     show_deleted = marshmallow.fields.Int(
144
         example=0,
163
         example=0,
145
         default=0,
164
         default=0,
146
         description='if set to 1, then show deleted contents.'
165
         description='if set to 1, then show deleted contents.'
147
-                    ' Default is 0 - hide deleted content'
166
+                    ' Default is 0 - hide deleted content',
167
+        validate=Range(min=0, max=1, error="Value must be 0 or 1"),
148
     )
168
     )
149
     show_active = marshmallow.fields.Int(
169
     show_active = marshmallow.fields.Int(
150
         example=1,
170
         example=1,
154
                     ' Note: active content are content '
174
                     ' Note: active content are content '
155
                     'that is neither archived nor deleted. '
175
                     'that is neither archived nor deleted. '
156
                     'The reason for this parameter to exist is for example '
176
                     'The reason for this parameter to exist is for example '
157
-                    'to allow to show only archived documents'
177
+                    'to allow to show only archived documents',
178
+        validate=Range(min=0, max=1, error="Value must be 0 or 1"),
158
     )
179
     )
159
 
180
 
160
     @post_load
181
     @post_load
263
 
284
 
264
 
285
 
265
 class WorkspaceDigestSchema(marshmallow.Schema):
286
 class WorkspaceDigestSchema(marshmallow.Schema):
266
-    workspace_id = marshmallow.fields.Int(example=4)
287
+    workspace_id = marshmallow.fields.Int(
288
+        example=4,
289
+        validate=Range(min=1, error="Value must be greater than 0"),
290
+    )
267
     slug = marshmallow.fields.String(example='intranet')
291
     slug = marshmallow.fields.String(example='intranet')
268
     label = marshmallow.fields.String(example='Intranet')
292
     label = marshmallow.fields.String(example='Intranet')
269
     sidebar_entries = marshmallow.fields.Nested(
293
     sidebar_entries = marshmallow.fields.Nested(
287
         example='contributor',
311
         example='contributor',
288
         validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
312
         validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
289
     )
313
     )
290
-    user_id = marshmallow.fields.Int(example=3)
291
-    workspace_id = marshmallow.fields.Int(example=4)
314
+    user_id = marshmallow.fields.Int(
315
+        example=3,
316
+        validate=Range(min=1, error="Value must be greater than 0"),
317
+    )
318
+    workspace_id = marshmallow.fields.Int(
319
+        example=4,
320
+        validate=Range(min=1, error="Value must be greater than 0"),
321
+    )
292
     user = marshmallow.fields.Nested(
322
     user = marshmallow.fields.Nested(
293
         UserDigestSchema()
323
         UserDigestSchema()
294
     )
324
     )
395
         description='id of the new parent content id.',
425
         description='id of the new parent content id.',
396
         allow_none=True,
426
         allow_none=True,
397
         required=True,
427
         required=True,
428
+        validate=Range(min=0, error="Value must be positive or 0"),
398
     )
429
     )
399
     new_workspace_id = marshmallow.fields.Int(
430
     new_workspace_id = marshmallow.fields.Int(
400
         example=2,
431
         example=2,
401
         description='id of the new workspace id.',
432
         description='id of the new workspace id.',
402
-        required=True
433
+        required=True,
434
+        validate=Range(min=1, error="Value must be greater than 0"),
403
     )
435
     )
404
 
436
 
405
     @post_load
437
     @post_load
423
 
455
 
424
 
456
 
425
 class ContentDigestSchema(marshmallow.Schema):
457
 class ContentDigestSchema(marshmallow.Schema):
426
-    content_id = marshmallow.fields.Int(example=6)
458
+    content_id = marshmallow.fields.Int(
459
+        example=6,
460
+        validate=Range(min=1, error="Value must be greater than 0"),
461
+    )
427
     slug = marshmallow.fields.Str(example='intervention-report-12')
462
     slug = marshmallow.fields.Str(example='intervention-report-12')
428
     parent_id = marshmallow.fields.Int(
463
     parent_id = marshmallow.fields.Int(
429
         example=34,
464
         example=34,
430
         allow_none=True,
465
         allow_none=True,
431
-        default=None
466
+        default=None,
467
+        validate=Range(min=0, error="Value must be positive or 0"),
432
     )
468
     )
433
     workspace_id = marshmallow.fields.Int(
469
     workspace_id = marshmallow.fields.Int(
434
         example=19,
470
         example=19,
471
+        validate=Range(min=1, error="Value must be greater than 0"),
435
     )
472
     )
436
     label = marshmallow.fields.Str(example='Intervention Report 12')
473
     label = marshmallow.fields.Str(example='Intervention Report 12')
437
     content_type = marshmallow.fields.Str(
474
     content_type = marshmallow.fields.Str(
498
 
535
 
499
 
536
 
500
 class RevisionSchema(ContentDigestSchema):
537
 class RevisionSchema(ContentDigestSchema):
501
-    comment_ids = marshmallow.fields.List(marshmallow.fields.Int(example=4))
502
-    revision_id = marshmallow.fields.Int(example=12)
538
+    comment_ids = marshmallow.fields.List(
539
+        marshmallow.fields.Int(
540
+            example=4,
541
+            validate=Range(min=1, error="Value must be greater than 0"),
542
+        )
543
+    )
544
+    revision_id = marshmallow.fields.Int(
545
+        example=12,
546
+        validate=Range(min=1, error="Value must be greater than 0"),
547
+    )
503
     created = marshmallow.fields.DateTime(
548
     created = marshmallow.fields.DateTime(
504
         format=DATETIME_FORMAT,
549
         format=DATETIME_FORMAT,
505
         description='Content creation date',
550
         description='Content creation date',
512
 
557
 
513
 
558
 
514
 class CommentSchema(marshmallow.Schema):
559
 class CommentSchema(marshmallow.Schema):
515
-    content_id = marshmallow.fields.Int(example=6)
516
-    parent_id = marshmallow.fields.Int(example=34)
560
+    content_id = marshmallow.fields.Int(
561
+        example=6,
562
+        validate=Range(min=1, error="Value must be greater than 0"),
563
+    )
564
+    parent_id = marshmallow.fields.Int(
565
+        example=34,
566
+        validate=Range(min=0, error="Value must be positive or 0"),
567
+    )
517
     raw_content = marshmallow.fields.String(
568
     raw_content = marshmallow.fields.String(
518
         example='<p>This is just an html comment !</p>'
569
         example='<p>This is just an html comment !</p>'
519
     )
570
     )