Browse Source

default endpoints for files

Guénaël Muller 6 years ago
parent
commit
621b8b79bf

+ 5 - 0
tracim/__init__.py View File

@@ -1,4 +1,6 @@
1 1
 # -*- coding: utf-8 -*-
2
+
3
+
2 4
 try:  # Python 3.5+
3 5
     from http import HTTPStatus
4 6
 except ImportError:
@@ -27,6 +29,7 @@ from tracim.views.core_api.system_controller import SystemController
27 29
 from tracim.views.core_api.user_controller import UserController
28 30
 from tracim.views.core_api.workspace_controller import WorkspaceController
29 31
 from tracim.views.contents_api.comment_controller import CommentController
32
+from tracim.views.contents_api.file_controller import FileController
30 33
 from tracim.views.errors import ErrorSchema
31 34
 from tracim.exceptions import NotAuthenticated
32 35
 from tracim.exceptions import InsufficientUserProfile
@@ -107,6 +110,7 @@ def web(global_config, **local_settings):
107 110
     comment_controller = CommentController()
108 111
     html_document_controller = HTMLDocumentController()
109 112
     thread_controller = ThreadController()
113
+    file_controller = FileController()
110 114
     configurator.include(session_controller.bind, route_prefix=BASE_API_V2)
111 115
     configurator.include(system_controller.bind, route_prefix=BASE_API_V2)
112 116
     configurator.include(user_controller.bind, route_prefix=BASE_API_V2)
@@ -114,6 +118,7 @@ def web(global_config, **local_settings):
114 118
     configurator.include(comment_controller.bind, route_prefix=BASE_API_V2)
115 119
     configurator.include(html_document_controller.bind, route_prefix=BASE_API_V2)  # nopep8
116 120
     configurator.include(thread_controller.bind, route_prefix=BASE_API_V2)
121
+    configurator.include(file_controller.bind, route_prefix=BASE_API_V2)
117 122
 
118 123
     hapic.add_documentation_view(
119 124
         '/api/v2/doc',

+ 515 - 0
tracim/tests/functional/test_contents.py View File

@@ -1,4 +1,12 @@
1 1
 # -*- coding: utf-8 -*-
2
+import transaction
3
+from depot.io.utils import FileIntent
4
+
5
+from tracim import models
6
+from tracim.lib.core.content import ContentApi
7
+from tracim.lib.core.workspace import WorkspaceApi
8
+from tracim.models.data import ContentType
9
+from tracim.models import get_tm_session
2 10
 from tracim.tests import FunctionalTest
3 11
 from tracim.tests import set_html_document_slug_to_legacy
4 12
 from tracim.fixtures.content import Content as ContentFixtures
@@ -429,6 +437,513 @@ class TestHtmlDocuments(FunctionalTest):
429 437
         )
430 438
 
431 439
 
440
+class TestFiles(FunctionalTest):
441
+    """
442
+    Tests for /api/v2/workspaces/{workspace_id}/files/{content_id}
443
+    endpoint
444
+    """
445
+
446
+    fixtures = [BaseFixture, ContentFixtures]
447
+
448
+    def test_api__get_file__ok_200__nominal_case(self) -> None:
449
+        """
450
+        Get one file of a content
451
+        """
452
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
453
+        admin = dbsession.query(models.User) \
454
+            .filter(models.User.email == 'admin@admin.admin') \
455
+            .one()
456
+        workspace_api = WorkspaceApi(
457
+            current_user=admin,
458
+            session=dbsession,
459
+            config=self.app_config
460
+        )
461
+        content_api = ContentApi(
462
+            current_user=admin,
463
+            session=dbsession,
464
+            config=self.app_config
465
+        )
466
+        business_workspace = workspace_api.get_one(1)
467
+        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
468
+        test_file = content_api.create(
469
+            content_type=ContentType.File,
470
+            workspace=business_workspace,
471
+            parent=tool_folder,
472
+            label='Test file',
473
+            do_save=False,
474
+            do_notify=False,
475
+        )
476
+        test_file.file_extension = '.txt'
477
+        test_file.depot_file = FileIntent(
478
+            b'Test file',
479
+            'Test_file.txt',
480
+            'text/plain',
481
+        )
482
+        content_api.update_content(test_file, 'Test_file', '<p>description</p>')  # nopep8
483
+        dbsession.flush()
484
+        transaction.commit()
485
+
486
+        self.testapp.authorization = (
487
+            'Basic',
488
+            (
489
+                'admin@admin.admin',
490
+                'admin@admin.admin'
491
+            )
492
+        )
493
+        res = self.testapp.get(
494
+            '/api/v2/workspaces/1/files/{}'.format(test_file.content_id),
495
+            status=200
496
+        )
497
+        content = res.json_body
498
+        assert content['content_type'] == 'file'
499
+        assert content['content_id'] == test_file.content_id
500
+        assert content['is_archived'] is False
501
+        assert content['is_deleted'] is False
502
+        assert content['label'] == 'Test_file'
503
+        assert content['parent_id'] == 1
504
+        assert content['show_in_ui'] is True
505
+        assert content['slug'] == 'test-file'
506
+        assert content['status'] == 'open'
507
+        assert content['workspace_id'] == 1
508
+        assert content['current_revision_id']
509
+        # TODO - G.M - 2018-06-173 - check date format
510
+        assert content['created']
511
+        assert content['author']
512
+        assert content['author']['user_id'] == 1
513
+        assert content['author']['avatar_url'] is None
514
+        assert content['author']['public_name'] == 'Global manager'
515
+        # TODO - G.M - 2018-06-173 - check date format
516
+        assert content['modified']
517
+        assert content['last_modifier'] == content['author']
518
+        assert content['raw_content'] == '<p>description</p>'  # nopep8
519
+
520
+    def test_api__get_files__err_400__wrong_content_type(self) -> None:
521
+        """
522
+        Get one file of a content content
523
+        """
524
+        self.testapp.authorization = (
525
+            'Basic',
526
+            (
527
+                'admin@admin.admin',
528
+                'admin@admin.admin'
529
+            )
530
+        )
531
+        res = self.testapp.get(
532
+            '/api/v2/workspaces/2/files/6',
533
+            status=400
534
+        )
535
+
536
+    def test_api__get_file__err_400__content_does_not_exist(self) -> None:  # nopep8
537
+        """
538
+        Get one file (content 170 does not exist in db
539
+        """
540
+        self.testapp.authorization = (
541
+            'Basic',
542
+            (
543
+                'admin@admin.admin',
544
+                'admin@admin.admin'
545
+            )
546
+        )
547
+        res = self.testapp.get(
548
+            '/api/v2/workspaces/1/files/170',
549
+            status=400
550
+        )
551
+
552
+    def test_api__get_file__err_400__content_not_in_workspace(self) -> None:  # nopep8
553
+        """
554
+        Get one file (content 9 is in workspace 2)
555
+        """
556
+        self.testapp.authorization = (
557
+            'Basic',
558
+            (
559
+                'admin@admin.admin',
560
+                'admin@admin.admin'
561
+            )
562
+        )
563
+        res = self.testapp.get(
564
+            '/api/v2/workspaces/1/files/9',
565
+            status=400
566
+        )
567
+
568
+    def test_api__get_file__err_400__workspace_does_not_exist(self) -> None:  # nopep8
569
+        """
570
+        Get one file (Workspace 40 does not exist)
571
+        """
572
+        self.testapp.authorization = (
573
+            'Basic',
574
+            (
575
+                'admin@admin.admin',
576
+                'admin@admin.admin'
577
+            )
578
+        )
579
+        res = self.testapp.get(
580
+            '/api/v2/workspaces/40/files/9',
581
+            status=400
582
+        )
583
+
584
+    def test_api__get_file__err_400__workspace_id_is_not_int(self) -> None:  # nopep8
585
+        """
586
+        Get one file, workspace id is not int
587
+        """
588
+        self.testapp.authorization = (
589
+            'Basic',
590
+            (
591
+                'admin@admin.admin',
592
+                'admin@admin.admin'
593
+            )
594
+        )
595
+        res = self.testapp.get(
596
+            '/api/v2/workspaces/coucou/files/9',
597
+            status=400
598
+        )
599
+
600
+    def test_api__get_file__err_400__content_id_is_not_int(self) -> None:  # nopep8
601
+        """
602
+        Get one file, content_id is not int
603
+        """
604
+        self.testapp.authorization = (
605
+            'Basic',
606
+            (
607
+                'admin@admin.admin',
608
+                'admin@admin.admin'
609
+            )
610
+        )
611
+        res = self.testapp.get(
612
+            '/api/v2/workspaces/2/files/coucou',
613
+            status=400
614
+        )
615
+
616
+    def test_api__update_file_info_err_400__empty_label(self) -> None:  # nopep8
617
+        """
618
+        Update(put) one file
619
+        """
620
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
621
+        admin = dbsession.query(models.User) \
622
+            .filter(models.User.email == 'admin@admin.admin') \
623
+            .one()
624
+        workspace_api = WorkspaceApi(
625
+            current_user=admin,
626
+            session=dbsession,
627
+            config=self.app_config
628
+        )
629
+        content_api = ContentApi(
630
+            current_user=admin,
631
+            session=dbsession,
632
+            config=self.app_config
633
+        )
634
+        business_workspace = workspace_api.get_one(1)
635
+        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
636
+        test_file = content_api.create(
637
+            content_type=ContentType.File,
638
+            workspace=business_workspace,
639
+            parent=tool_folder,
640
+            label='Test file',
641
+            do_save=False,
642
+            do_notify=False,
643
+        )
644
+        test_file.file_extension = '.txt'
645
+        test_file.depot_file = FileIntent(
646
+            b'Test file',
647
+            'Test_file.txt',
648
+            'text/plain',
649
+        )
650
+        content_api.update_content(test_file, 'Test_file', '<p>description</p>')  # nopep8
651
+        dbsession.flush()
652
+        transaction.commit()
653
+
654
+        self.testapp.authorization = (
655
+            'Basic',
656
+            (
657
+                'admin@admin.admin',
658
+                'admin@admin.admin'
659
+            )
660
+        )
661
+        params = {
662
+            'label': '',
663
+            'raw_content': '<p> Le nouveau contenu </p>',
664
+        }
665
+        res = self.testapp.put_json(
666
+            '/api/v2/workspaces/1/files/{}'.format(test_file.content_id),
667
+            params=params,
668
+            status=400
669
+        )
670
+
671
+    def test_api__update_file_info__ok_200__nominal_case(self) -> None:
672
+        """
673
+        Update(put) one file
674
+        """
675
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
676
+        admin = dbsession.query(models.User) \
677
+            .filter(models.User.email == 'admin@admin.admin') \
678
+            .one()
679
+        workspace_api = WorkspaceApi(
680
+            current_user=admin,
681
+            session=dbsession,
682
+            config=self.app_config
683
+        )
684
+        content_api = ContentApi(
685
+            current_user=admin,
686
+            session=dbsession,
687
+            config=self.app_config
688
+        )
689
+        business_workspace = workspace_api.get_one(1)
690
+        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
691
+        test_file = content_api.create(
692
+            content_type=ContentType.File,
693
+            workspace=business_workspace,
694
+            parent=tool_folder,
695
+            label='Test file',
696
+            do_save=False,
697
+            do_notify=False,
698
+        )
699
+        test_file.file_extension = '.txt'
700
+        test_file.depot_file = FileIntent(
701
+            b'Test file',
702
+            'Test_file.txt',
703
+            'text/plain',
704
+        )
705
+        content_api.update_content(test_file, 'Test_file', '<p>description</p>')  # nopep8
706
+        dbsession.flush()
707
+        transaction.commit()
708
+
709
+        self.testapp.authorization = (
710
+            'Basic',
711
+            (
712
+                'admin@admin.admin',
713
+                'admin@admin.admin'
714
+            )
715
+        )
716
+        params = {
717
+            'label': 'My New label',
718
+            'raw_content': '<p> Le nouveau contenu </p>',
719
+        }
720
+        res = self.testapp.put_json(
721
+            '/api/v2/workspaces/1/files/{}'.format(test_file.content_id),
722
+            params=params,
723
+            status=200
724
+        )
725
+        content = res.json_body
726
+        assert content['content_type'] == 'file'
727
+        assert content['content_id'] == test_file.content_id
728
+        assert content['is_archived'] is False
729
+        assert content['is_deleted'] is False
730
+        assert content['label'] == 'My New label'
731
+        assert content['parent_id'] == 1
732
+        assert content['show_in_ui'] is True
733
+        assert content['slug'] == 'my-new-label'
734
+        assert content['status'] == 'open'
735
+        assert content['workspace_id'] == 1
736
+        assert content['current_revision_id']
737
+        # TODO - G.M - 2018-06-173 - check date format
738
+        assert content['created']
739
+        assert content['author']
740
+        assert content['author']['user_id'] == 1
741
+        assert content['author']['avatar_url'] is None
742
+        assert content['author']['public_name'] == 'Global manager'
743
+        # TODO - G.M - 2018-06-173 - check date format
744
+        assert content['modified']
745
+        assert content['last_modifier'] == content['author']
746
+        assert content['raw_content'] == '<p> Le nouveau contenu </p>'
747
+
748
+        res = self.testapp.get(
749
+            '/api/v2/workspaces/1/files/{}'.format(test_file.content_id),
750
+            status=200
751
+        )
752
+        content = res.json_body
753
+        assert content['content_type'] == 'file'
754
+        assert content['content_id'] == test_file.content_id
755
+        assert content['is_archived'] is False
756
+        assert content['is_deleted'] is False
757
+        assert content['label'] == 'My New label'
758
+        assert content['parent_id'] == 1
759
+        assert content['show_in_ui'] is True
760
+        assert content['slug'] == 'my-new-label'
761
+        assert content['status'] == 'open'
762
+        assert content['workspace_id'] == 1
763
+        assert content['current_revision_id']
764
+        # TODO - G.M - 2018-06-173 - check date format
765
+        assert content['created']
766
+        assert content['author']
767
+        assert content['author']['user_id'] == 1
768
+        assert content['author']['avatar_url'] is None
769
+        assert content['author']['public_name'] == 'Global manager'
770
+        # TODO - G.M - 2018-06-173 - check date format
771
+        assert content['modified']
772
+        assert content['last_modifier'] == content['author']
773
+        assert content['raw_content'] == '<p> Le nouveau contenu </p>'
774
+
775
+    def test_api__get_file_revisions__ok_200__nominal_case(
776
+            self
777
+    ) -> None:
778
+        """
779
+        Get file revisions
780
+        """
781
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
782
+        admin = dbsession.query(models.User) \
783
+            .filter(models.User.email == 'admin@admin.admin') \
784
+            .one()
785
+        workspace_api = WorkspaceApi(
786
+            current_user=admin,
787
+            session=dbsession,
788
+            config=self.app_config
789
+        )
790
+        content_api = ContentApi(
791
+            current_user=admin,
792
+            session=dbsession,
793
+            config=self.app_config
794
+        )
795
+        business_workspace = workspace_api.get_one(1)
796
+        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
797
+        test_file = content_api.create(
798
+            content_type=ContentType.File,
799
+            workspace=business_workspace,
800
+            parent=tool_folder,
801
+            label='Test file',
802
+            do_save=False,
803
+            do_notify=False,
804
+        )
805
+        test_file.file_extension = '.txt'
806
+        test_file.depot_file = FileIntent(
807
+            b'Test file',
808
+            'Test_file.txt',
809
+            'text/plain',
810
+        )
811
+        content_api.update_content(test_file, 'Test_file', '<p>description</p>')  # nopep8
812
+        dbsession.flush()
813
+        transaction.commit()
814
+
815
+        self.testapp.authorization = (
816
+            'Basic',
817
+            (
818
+                'admin@admin.admin',
819
+                'admin@admin.admin'
820
+            )
821
+        )
822
+        res = self.testapp.get(
823
+            '/api/v2/workspaces/1/files/{}/revisions'.format(test_file.content_id),
824
+            status=200
825
+        )
826
+        revisions = res.json_body
827
+        assert len(revisions) == 1
828
+        revision = revisions[0]
829
+        assert revision['content_type'] == 'file'
830
+        assert revision['content_id'] == test_file.content_id
831
+        assert revision['is_archived'] is False
832
+        assert revision['is_deleted'] is False
833
+        assert revision['label'] == 'Test_file'
834
+        assert revision['parent_id'] == 1
835
+        assert revision['show_in_ui'] is True
836
+        assert revision['slug'] == 'test-file'
837
+        assert revision['status'] == 'open'
838
+        assert revision['workspace_id'] == 1
839
+        assert revision['revision_id']
840
+        assert revision['sub_content_types']
841
+        # TODO - G.M - 2018-06-173 - Test with real comments
842
+        assert revision['comment_ids'] == []
843
+        # TODO - G.M - 2018-06-173 - check date format
844
+        assert revision['created']
845
+        assert revision['author']
846
+        assert revision['author']['user_id'] == 1
847
+        assert revision['author']['avatar_url'] is None
848
+        assert revision['author']['public_name'] == 'Global manager'
849
+
850
+    def test_api__set_file_status__ok_200__nominal_case(self) -> None:
851
+        """
852
+        set file status
853
+        """
854
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
855
+        admin = dbsession.query(models.User) \
856
+            .filter(models.User.email == 'admin@admin.admin') \
857
+            .one()
858
+        workspace_api = WorkspaceApi(
859
+            current_user=admin,
860
+            session=dbsession,
861
+            config=self.app_config
862
+        )
863
+        content_api = ContentApi(
864
+            current_user=admin,
865
+            session=dbsession,
866
+            config=self.app_config
867
+        )
868
+        business_workspace = workspace_api.get_one(1)
869
+        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
870
+        test_file = content_api.create(
871
+            content_type=ContentType.File,
872
+            workspace=business_workspace,
873
+            parent=tool_folder,
874
+            label='Test file',
875
+            do_save=False,
876
+            do_notify=False,
877
+        )
878
+        test_file.file_extension = '.txt'
879
+        test_file.depot_file = FileIntent(
880
+            b'Test file',
881
+            'Test_file.txt',
882
+            'text/plain',
883
+        )
884
+        content_api.update_content(test_file, 'Test_file', '<p>description</p>')  # nopep8
885
+        dbsession.flush()
886
+        transaction.commit()
887
+
888
+        self.testapp.authorization = (
889
+            'Basic',
890
+            (
891
+                'admin@admin.admin',
892
+                'admin@admin.admin'
893
+            )
894
+        )
895
+        params = {
896
+            'status': 'closed-deprecated',
897
+        }
898
+
899
+        # before
900
+        res = self.testapp.get(
901
+            '/api/v2/workspaces/1/files/{}'.format(test_file.content_id),
902
+            status=200
903
+        )
904
+        content = res.json_body
905
+        assert content['content_type'] == 'file'
906
+        assert content['content_id'] == test_file.content_id
907
+        assert content['status'] == 'open'
908
+
909
+        # set status
910
+        res = self.testapp.put_json(
911
+            '/api/v2/workspaces/1/files/{}/status'.format(test_file.content_id),
912
+            params=params,
913
+            status=204
914
+        )
915
+
916
+        # after
917
+        res = self.testapp.get(
918
+            '/api/v2/workspaces/1/files/{}'.format(test_file.content_id),
919
+            status=200
920
+        )
921
+        content = res.json_body
922
+        assert content['content_type'] == 'file'
923
+        assert content['content_id'] == test_file.content_id
924
+        assert content['status'] == 'closed-deprecated'
925
+
926
+    def test_api__set_file_status__err_400__wrong_status(self) -> None:
927
+        """
928
+        set file status
929
+        """
930
+        self.testapp.authorization = (
931
+            'Basic',
932
+            (
933
+                'admin@admin.admin',
934
+                'admin@admin.admin'
935
+            )
936
+        )
937
+        params = {
938
+            'status': 'unexistant-status',
939
+        }
940
+        res = self.testapp.put_json(
941
+            '/api/v2/workspaces/2/files/6/status',
942
+            params=params,
943
+            status=400
944
+        )
945
+
946
+
432 947
 class TestThreads(FunctionalTest):
433 948
     """
434 949
     Tests for /api/v2/workspaces/{workspace_id}/threads/{content_id}

+ 81 - 30
tracim/views/contents_api/file_controller.py View File

@@ -3,6 +3,8 @@ import typing
3 3
 
4 4
 import transaction
5 5
 from pyramid.config import Configurator
6
+
7
+from tracim.exceptions import EmptyLabelNotAllowed
6 8
 from tracim.models.data import UserRoleInWorkspace
7 9
 
8 10
 try:  # Python 3.5+
@@ -17,15 +19,11 @@ from tracim.views.controllers import Controller
17 19
 from tracim.views.core_api.schemas import FileContentSchema
18 20
 from tracim.views.core_api.schemas import FileRevisionSchema
19 21
 from tracim.views.core_api.schemas import SetContentStatusSchema
20
-from tracim.views.core_api.schemas import FileModifySchema
22
+from tracim.views.core_api.schemas import FileContentModifySchema
21 23
 from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
22 24
 from tracim.views.core_api.schemas import NoContentSchema
23 25
 from tracim.lib.utils.authorization import require_content_types
24 26
 from tracim.lib.utils.authorization import require_workspace_role
25
-from tracim.exceptions import WorkspaceNotFound, ContentTypeNotAllowed
26
-from tracim.exceptions import InsufficientUserRoleInWorkspace
27
-from tracim.exceptions import NotAuthenticated
28
-from tracim.exceptions import AuthenticationFailed
29 27
 from tracim.models.context_models import ContentInContext
30 28
 from tracim.models.context_models import RevisionInContext
31 29
 from tracim.models.contents import ContentTypeLegacy as ContentType
@@ -37,17 +35,65 @@ FILE_ENDPOINTS_TAG = 'Files'
37 35
 
38 36
 class FileController(Controller):
39 37
 
38
+    # # File data
39
+    # @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
40
+    # @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
41
+    # @require_content_types([file_type])
42
+    # @hapic.input_path(WorkspaceAndContentIdPathSchema())
43
+    # #@hapic.input_files()
44
+    # @hapic.output_file([])
45
+    # def upload_file(self, context, request: TracimRequest, hapic_data=None):
46
+    #     # TODO - G.M - 2018-07-05 - Do this endpoint
47
+    #     app_config = request.registry.settings['CFG']
48
+    #     api = ContentApi(
49
+    #         current_user=request.current_user,
50
+    #         session=request.dbsession,
51
+    #         config=app_config,
52
+    #     )
53
+    #     content = api.get_one(
54
+    #         hapic_data.path.content_id,
55
+    #         content_type=ContentType.Any
56
+    #     )
57
+    #     file = request.POST['files']
58
+    #     api.update_file_data(
59
+    #         content,
60
+    #         new_filename=file.filename,
61
+    #         new_mimetype=file.type,
62
+    #         new_content=file.file,
63
+    #     )
64
+    #     return content.depot_file
65
+    #
66
+    # @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
67
+    # @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
68
+    # @require_content_types([file_type])
69
+    # @hapic.input_path(WorkspaceAndContentIdPathSchema())
70
+    # @hapic.output_file([])
71
+    # def download_file(self, context, request: TracimRequest, hapic_data=None):
72
+    #     # TODO - G.M - 2018-07-05 - Do this endpoint
73
+    #     app_config = request.registry.settings['CFG']
74
+    #     api = ContentApi(
75
+    #         current_user=request.current_user,
76
+    #         session=request.dbsession,
77
+    #         config=app_config,
78
+    #     )
79
+    #     content = api.get_one(
80
+    #         hapic_data.path.content_id,
81
+    #         content_type=ContentType.Any
82
+    #     )
83
+    #     return content.depot_file
84
+
85
+    # Previews
86
+    # def get_file_preview(self):
87
+    #     # TODO - G.M - 2018-07-05 - Do this endpoint
88
+    #     pass
89
+    
90
+    # File infos
40 91
     @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
41
-    @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
42
-    @hapic.handle_exception(InsufficientUserRoleInWorkspace, HTTPStatus.FORBIDDEN)
43
-    @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
44
-    @hapic.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN)
45
-    @hapic.handle_exception(ContentTypeNotAllowed, HTTPStatus.BAD_REQUEST)
46 92
     @require_workspace_role(UserRoleInWorkspace.READER)
47 93
     @require_content_types([file_type])
48 94
     @hapic.input_path(WorkspaceAndContentIdPathSchema())
49 95
     @hapic.output_body(FileContentSchema())
50
-    def get_file(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext:  # nopep8
96
+    def get_file_infos(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext:  # nopep8
51 97
         """
52 98
         Get thread content
53 99
         """
@@ -64,16 +110,13 @@ class FileController(Controller):
64 110
         return api.get_content_in_context(content)
65 111
 
66 112
     @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
67
-    @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
68
-    @hapic.handle_exception(InsufficientUserRoleInWorkspace, HTTPStatus.FORBIDDEN)
69
-    @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
70
-    @hapic.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN)
113
+    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
71 114
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
72 115
     @require_content_types([file_type])
73 116
     @hapic.input_path(WorkspaceAndContentIdPathSchema())
74
-    @hapic.input_body(FileModifySchema())
117
+    @hapic.input_body(FileContentModifySchema())
75 118
     @hapic.output_body(FileContentSchema())
76
-    def update_file(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext:  # nopep8
119
+    def update_file_info(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext:  # nopep8
77 120
         """
78 121
         update thread
79 122
         """
@@ -102,10 +145,6 @@ class FileController(Controller):
102 145
         return api.get_content_in_context(content)
103 146
 
104 147
     @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
105
-    @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
106
-    @hapic.handle_exception(InsufficientUserRoleInWorkspace, HTTPStatus.FORBIDDEN)
107
-    @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
108
-    @hapic.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN)
109 148
     @require_workspace_role(UserRoleInWorkspace.READER)
110 149
     @require_content_types([file_type])
111 150
     @hapic.input_path(WorkspaceAndContentIdPathSchema())
@@ -136,10 +175,7 @@ class FileController(Controller):
136 175
         ]
137 176
 
138 177
     @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
139
-    @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
140
-    @hapic.handle_exception(InsufficientUserRoleInWorkspace, HTTPStatus.FORBIDDEN)
141
-    @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
142
-    @hapic.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN)
178
+    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
143 179
     @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
144 180
     @require_content_types([file_type])
145 181
     @hapic.input_path(WorkspaceAndContentIdPathSchema())
@@ -172,22 +208,37 @@ class FileController(Controller):
172 208
         return
173 209
 
174 210
     def bind(self, configurator: Configurator) -> None:
175
-        # Get file
211
+        # Get file info
176 212
         configurator.add_route(
177
-            'file',
213
+            'file_info',
178 214
             '/workspaces/{workspace_id}/files/{content_id}',
179 215
             request_method='GET'
180 216
         )
181
-        configurator.add_view(self.get_file, route_name='file')  # nopep8
217
+        configurator.add_view(self.get_file_infos, route_name='file_info')  # nopep8
182 218
 
183 219
         # update file
184 220
         configurator.add_route(
185
-            'update_file',
221
+            'update_file_info',
186 222
             '/workspaces/{workspace_id}/files/{content_id}',
187 223
             request_method='PUT'
188 224
         )  # nopep8
189
-        configurator.add_view(self.update_file, route_name='update_file')  # nopep8
225
+        configurator.add_view(self.update_file_info, route_name='update_file_info')  # nopep8
190 226
 
227
+        # # upload new file data
228
+        # configurator.add_route(
229
+        #     'upload_file',
230
+        #     '/workspaces/{workspace_id}/files/{content_id}/file_data',  # nopep8
231
+        #     request_method='PUT'
232
+        # )
233
+        # configurator.add_view(self.upload_file, route_name='upload_file')  # nopep8
234
+        #
235
+        # # download file data
236
+        # configurator.add_route(
237
+        #     'download_file',
238
+        #     '/workspaces/{workspace_id}/files/{content_id}/file_data',  # nopep8
239
+        #     request_method='GET'
240
+        # )
241
+        # configurator.add_view(self.download_file, route_name='download_file')  # nopep8
191 242
         # get file revisions
192 243
         configurator.add_route(
193 244
             'file_revisions',

+ 17 - 0
tracim/views/core_api/schemas.py View File

@@ -411,10 +411,19 @@ class TextBasedDataAbstractSchema(marshmallow.Schema):
411 411
     )
412 412
 
413 413
 
414
+class FileInfoAbstractSchema(marshmallow.Schema):
415
+    raw_content = marshmallow.fields.String(
416
+        description='raw text or html description of the file'
417
+    )
418
+
419
+
414 420
 class TextBasedContentSchema(ContentSchema, TextBasedDataAbstractSchema):
415 421
     pass
416 422
 
417 423
 
424
+class FileContentSchema(ContentSchema, FileInfoAbstractSchema):
425
+    pass
426
+
418 427
 #####
419 428
 # Revision
420 429
 #####
@@ -434,6 +443,10 @@ class TextBasedRevisionSchema(RevisionSchema, TextBasedDataAbstractSchema):
434 443
     pass
435 444
 
436 445
 
446
+class FileRevisionSchema(RevisionSchema, FileInfoAbstractSchema):
447
+    pass
448
+
449
+
437 450
 class CommentSchema(marshmallow.Schema):
438 451
     content_id = marshmallow.fields.Int(example=6)
439 452
     parent_id = marshmallow.fields.Int(example=34)
@@ -472,6 +485,10 @@ class TextBasedContentModifySchema(ContentModifyAbstractSchema, TextBasedDataAbs
472 485
         return TextBasedContentUpdate(**data)
473 486
 
474 487
 
488
+class FileContentModifySchema(TextBasedContentModifySchema):
489
+    pass
490
+
491
+
475 492
 class SetContentStatusSchema(marshmallow.Schema):
476 493
     status = marshmallow.fields.Str(
477 494
         example='closed-deprecated',