Pārlūkot izejas kodu

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

Guénaël Muller 6 gadus atpakaļ
vecāks
revīzija
5e9d61ce64

+ 2 - 0
tracim/__init__.py Parādīt failu

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)

+ 20 - 1
tracim/exceptions.py Parādīt failu

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

+ 22 - 9
tracim/lib/core/content.py Parādīt failu

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 Parādīt failu

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 Parādīt failu

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

+ 1 - 0
tracim/tests/functional/test_comments.py Parādīt failu

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 Parādīt failu

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 Parādīt failu

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 Parādīt failu

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
 
166
 
146
     @post_load
167
     @post_load
204
 
225
 
205
 
226
 
206
 class WorkspaceDigestSchema(marshmallow.Schema):
227
 class WorkspaceDigestSchema(marshmallow.Schema):
207
-    workspace_id = marshmallow.fields.Int(example=4)
228
+    workspace_id = marshmallow.fields.Int(
229
+        example=4,
230
+        validate=Range(min=1, error="Value must be greater than 0"),
231
+    )
208
     slug = marshmallow.fields.String(example='intranet')
232
     slug = marshmallow.fields.String(example='intranet')
209
     label = marshmallow.fields.String(example='Intranet')
233
     label = marshmallow.fields.String(example='Intranet')
210
     sidebar_entries = marshmallow.fields.Nested(
234
     sidebar_entries = marshmallow.fields.Nested(
228
         example='contributor',
252
         example='contributor',
229
         validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
253
         validate=OneOf(UserRoleInWorkspace.get_all_role_slug())
230
     )
254
     )
231
-    user_id = marshmallow.fields.Int(example=3)
232
-    workspace_id = marshmallow.fields.Int(example=4)
255
+    user_id = marshmallow.fields.Int(
256
+        example=3,
257
+        validate=Range(min=1, error="Value must be greater than 0"),
258
+    )
259
+    workspace_id = marshmallow.fields.Int(
260
+        example=4,
261
+        validate=Range(min=1, error="Value must be greater than 0"),
262
+    )
233
     user = marshmallow.fields.Nested(
263
     user = marshmallow.fields.Nested(
234
         UserSchema(only=('public_name', 'avatar_url'))
264
         UserSchema(only=('public_name', 'avatar_url'))
235
     )
265
     )
318
         description='id of the new parent content id.',
348
         description='id of the new parent content id.',
319
         allow_none=True,
349
         allow_none=True,
320
         required=True,
350
         required=True,
351
+        validate=Range(min=0, error="Value must be positive or 0"),
321
     )
352
     )
322
     new_workspace_id = marshmallow.fields.Int(
353
     new_workspace_id = marshmallow.fields.Int(
323
         example=2,
354
         example=2,
324
         description='id of the new workspace id.',
355
         description='id of the new workspace id.',
325
-        required=True
356
+        required=True,
357
+        validate=Range(min=1, error="Value must be greater than 0"),
326
     )
358
     )
327
 
359
 
328
     @post_load
360
     @post_load
346
 
378
 
347
 
379
 
348
 class ContentDigestSchema(marshmallow.Schema):
380
 class ContentDigestSchema(marshmallow.Schema):
349
-    content_id = marshmallow.fields.Int(example=6)
381
+    content_id = marshmallow.fields.Int(
382
+        example=6,
383
+        validate=Range(min=1, error="Value must be greater than 0"),
384
+    )
350
     slug = marshmallow.fields.Str(example='intervention-report-12')
385
     slug = marshmallow.fields.Str(example='intervention-report-12')
351
     parent_id = marshmallow.fields.Int(
386
     parent_id = marshmallow.fields.Int(
352
         example=34,
387
         example=34,
353
         allow_none=True,
388
         allow_none=True,
354
-        default=None
389
+        default=None,
390
+        validate=Range(min=0, error="Value must be positive or 0"),
355
     )
391
     )
356
     workspace_id = marshmallow.fields.Int(
392
     workspace_id = marshmallow.fields.Int(
357
         example=19,
393
         example=19,
394
+        validate=Range(min=1, error="Value must be greater than 0"),
358
     )
395
     )
359
     label = marshmallow.fields.Str(example='Intervention Report 12')
396
     label = marshmallow.fields.Str(example='Intervention Report 12')
360
     content_type = marshmallow.fields.Str(
397
     content_type = marshmallow.fields.Str(
421
 
458
 
422
 
459
 
423
 class RevisionSchema(ContentDigestSchema):
460
 class RevisionSchema(ContentDigestSchema):
424
-    comment_ids = marshmallow.fields.List(marshmallow.fields.Int(example=4))
425
-    revision_id = marshmallow.fields.Int(example=12)
461
+    comment_ids = marshmallow.fields.List(
462
+        marshmallow.fields.Int(
463
+            example=4,
464
+            validate=Range(min=1, error="Value must be greater than 0"),
465
+        )
466
+    )
467
+    revision_id = marshmallow.fields.Int(
468
+        example=12,
469
+        validate=Range(min=1, error="Value must be greater than 0"),
470
+    )
426
     created = marshmallow.fields.DateTime(
471
     created = marshmallow.fields.DateTime(
427
         format=DATETIME_FORMAT,
472
         format=DATETIME_FORMAT,
428
         description='Content creation date',
473
         description='Content creation date',
435
 
480
 
436
 
481
 
437
 class CommentSchema(marshmallow.Schema):
482
 class CommentSchema(marshmallow.Schema):
438
-    content_id = marshmallow.fields.Int(example=6)
439
-    parent_id = marshmallow.fields.Int(example=34)
483
+    content_id = marshmallow.fields.Int(
484
+        example=6,
485
+        validate=Range(min=1, error="Value must be greater than 0"),
486
+    )
487
+    parent_id = marshmallow.fields.Int(
488
+        example=34,
489
+        validate=Range(min=0, error="Value must be positive or 0"),
490
+    )
440
     raw_content = marshmallow.fields.String(
491
     raw_content = marshmallow.fields.String(
441
         example='<p>This is just an html comment !</p>'
492
         example='<p>This is just an html comment !</p>'
442
     )
493
     )