Browse Source

tests,schema,controller for new api endpoints

Guénaël Muller 6 years ago
parent
commit
3972eff01d

+ 3 - 0
tracim/__init__.py View File

@@ -15,6 +15,7 @@ from tracim.lib.utils.authorization import AcceptAllAuthorizationPolicy
15 15
 from tracim.lib.utils.authorization import TRACIM_DEFAULT_PERM
16 16
 from tracim.views import BASE_API_V2
17 17
 from tracim.views.core_api.session_controller import SessionController
18
+from tracim.views.core_api.system_controller import SystemController
18 19
 from tracim.views.errors import ErrorSchema
19 20
 from tracim.lib.utils.cors import add_cors_support
20 21
 
@@ -57,7 +58,9 @@ def main(global_config, **settings):
57 58
     )
58 59
     # Add controllers
59 60
     session_api = SessionController()
61
+    system_api = SystemController()
60 62
     configurator.include(session_api.bind, route_prefix=BASE_API_V2)
63
+    configurator.include(system_api.bind, route_prefix=BASE_API_V2)
61 64
     hapic.add_documentation_view(
62 65
         '/api/v2/doc',
63 66
         'Tracim v2 API',

+ 96 - 0
tracim/models/applications.py View File

@@ -0,0 +1,96 @@
1
+# coding=utf-8
2
+import typing
3
+
4
+
5
+class Application(object):
6
+    """
7
+    Application class with data needed for frontend
8
+    """
9
+    def __init__(
10
+            self,
11
+            label: str,
12
+            slug: str,
13
+            icon: str,
14
+            hexcolor: str,
15
+            is_active: bool,
16
+            config: typing.Dict[str, str],
17
+            routes: typing.List[str],
18
+    ) -> None:
19
+        self.label = label
20
+        self.slug = slug
21
+        self.icon = icon
22
+        self.hexcolor = hexcolor
23
+        self.is_active = is_active
24
+        self.config = config
25
+        self.routes = routes
26
+
27
+
28
+# TODO - G.M - 21-05-2018 Do not rely on hardcoded app list
29
+# default apps
30
+calendar = Application(
31
+    label='Calendar',
32
+    slug='calendar',
33
+    icon='calendar-alt',
34
+    hexcolor='#757575',
35
+    is_active=True,
36
+    config={},
37
+    routes=[
38
+        '/#/workspaces/{workspace_id}/calendar',
39
+    ],
40
+)
41
+
42
+thread = Application(
43
+    label='Threads',
44
+    slug='contents/threads',
45
+    icon='comments-o',
46
+    hexcolor='#ad4cf9',
47
+    is_active=True,
48
+    config={},
49
+    routes=[
50
+        '/#/workspaces/{workspace_id}/contents?type=thread',
51
+    ],
52
+)
53
+
54
+file = Application(
55
+    label='Files',
56
+    slug='contents/files',
57
+    icon='paperclip',
58
+    hexcolor='#FF9900',
59
+    is_active=True,
60
+    config={},
61
+    routes=[
62
+        '/#/workspaces/{workspace_id}/contents?type=file',
63
+    ],
64
+)
65
+
66
+pagemarkdownplus = Application(
67
+    label='Rich Markdown Files',  # TODO : Better label
68
+    slug='contents/pagemarkdownplus',
69
+    icon='file-code',
70
+    hexcolor='#f12d2d',
71
+    is_active=True,
72
+    config={},
73
+    routes=[
74
+        '/#/workspaces/{workspace_id}/contents?type=file',
75
+    ],
76
+)
77
+
78
+pagehtml = Application(
79
+    label='Text Documents',  # TODO : Better label
80
+    slug='contents/pagehtml',
81
+    icon='file-text-o',
82
+    hexcolor='#3f52e3',
83
+    is_active=True,
84
+    config={},
85
+    routes=[
86
+        '/#/workspaces/{workspace_id}/contents?type=file',
87
+    ],
88
+)
89
+# List of applications
90
+applications = [
91
+    pagehtml,
92
+    pagemarkdownplus,
93
+    file,
94
+    thread,
95
+    calendar,
96
+]

+ 38 - 0
tracim/models/workspace_menu_entries.py View File

@@ -0,0 +1,38 @@
1
+# coding=utf-8
2
+
3
+
4
+class WorkspaceMenuEntry(object):
5
+    """
6
+    Application class with data needed for frontend
7
+    """
8
+    def __init__(
9
+            self,
10
+            label: str,
11
+            slug: str,
12
+            icon: str,
13
+            hexcolor: str,
14
+            route: str,
15
+    ) -> None:
16
+        self.slug = slug
17
+        self.label = label
18
+        self.route = route
19
+        self.hexcolor = hexcolor
20
+        self.icon = icon
21
+
22
+
23
+dashboard_menu_entry = WorkspaceMenuEntry(
24
+  slug='dashboard',
25
+  label='Dashboard',
26
+  route='/#/workspaces/{workspace_id}/dashboard',
27
+  hexcolor='#252525',
28
+  icon="",
29
+)
30
+all_content_menu_entry = WorkspaceMenuEntry(
31
+  slug="contents/all",
32
+  label="Tous les contenus",
33
+  route="/#/workspaces/{workspace_id}/contents",
34
+  hexcolor="#fdfdfd",
35
+  icon="",
36
+)
37
+
38
+

+ 51 - 0
tracim/tests/functional/test_system.py View File

@@ -0,0 +1,51 @@
1
+# coding=utf-8
2
+from tracim.tests import FunctionalTest
3
+
4
+
5
+class TestApplicationsEndpoint(FunctionalTest):
6
+    def test_api__get_applications__ok_200__nominal_case(self):
7
+        # TODO need authorization ? check permissions ?
8
+        # self.testapp.authorization = (
9
+        #     'Basic',
10
+        #     (
11
+        #         'admin@admin.admin',
12
+        #         'admin@admin.admin'
13
+        #     )
14
+        # )
15
+        res = self.testapp.get('/api/v2/system/applications', status=200)
16
+        res = res.json_body
17
+        application = res[0]
18
+        assert application['label'] == "Text Documents"
19
+        assert application['slug'] == 'contents/pagehtml'
20
+        assert application['icon'] == 'file-text-o'
21
+        assert application['hexcolor'] == '#3f52e3'
22
+        assert application['is_active'] is True
23
+        assert 'config' in application
24
+        application = res[1]
25
+        assert application['label'] == "Rich Markdown Files"
26
+        assert application['slug'] == 'contents/pagemarkdownplus'
27
+        assert application['icon'] == 'file-code'
28
+        assert application['hexcolor'] == '#f12d2d'
29
+        assert application['is_active'] is True
30
+        assert 'config' in application
31
+        application = res[2]
32
+        assert application['label'] == "Files"
33
+        assert application['slug'] == 'contents/files'
34
+        assert application['icon'] == 'paperclip'
35
+        assert application['hexcolor'] == '#FF9900'
36
+        assert application['is_active'] is True
37
+        assert 'config' in application
38
+        application = res[3]
39
+        assert application['label'] == "Threads"
40
+        assert application['slug'] == 'contents/threads'
41
+        assert application['icon'] == 'comments-o'
42
+        assert application['hexcolor'] == '#ad4cf9'
43
+        assert application['is_active'] is True
44
+        assert 'config' in application
45
+        application = res[4]
46
+        assert application['label'] == "Calendar"
47
+        assert application['slug'] == 'calendar'
48
+        assert application['icon'] == 'calendar-alt'
49
+        assert application['hexcolor'] == '#757575'
50
+        assert application['is_active'] is True
51
+        assert 'config' in application

+ 70 - 0
tracim/tests/functional/test_user.py View File

@@ -0,0 +1,70 @@
1
+# coding=utf-8
2
+from tracim.tests import FunctionalTest
3
+
4
+
5
+class TestUserWorkspaceEndpoint(FunctionalTest):
6
+    def test_api__get_user_workspaces__ok_200__nominal_case(self):
7
+        self.testapp.authorization = (
8
+            'Basic',
9
+            (
10
+                'admin@admin.admin',
11
+                'admin@admin.admin'
12
+            )
13
+        )
14
+        res = self.testapp.post_json('/api/v2/users/1/workspaces', status=200)
15
+        workspace = res[0]
16
+        assert workspace['id'] == 1
17
+        assert workspace['slug'] == 'w1'
18
+        assert workspace['label'] == 'w1'
19
+        assert workspace['description'] == 'Just another description'
20
+        assert len(workspace['sidebar_entries']) == 3  # TODO change this
21
+
22
+        sidebar_entry = workspace['sidebar_entries'][0]
23
+        assert sidebar_entry['slug'] == 'markdown-pages'
24
+        assert sidebar_entry['label'] == 'Document Markdown'
25
+        assert sidebar_entry['route'] == "/#/workspace/{workspace_id}/contents/?type=mardown-page"  # nopep8
26
+        assert sidebar_entry['hexcolor'] == "#F0F9DC"
27
+        assert sidebar_entry['icon'] == "file-text-o"
28
+        # TODO To this for the other
29
+
30
+    def test_api__get_user_workspaces__err_403__unallowed_user(self):
31
+        self.testapp.authorization = (
32
+            'Basic',
33
+            (
34
+                'lawrence-not-real-email@fsf.local',
35
+                'foobarbaz'
36
+            )
37
+        )
38
+        res = self.testapp.post_json('/api/v2/users/1/workspaces', status=403)
39
+        assert isinstance(res.json, dict)
40
+        assert 'code' in res.json.keys()
41
+        assert 'message' in res.json.keys()
42
+        assert 'details' in res.json.keys()
43
+
44
+    def test_api__get_user_workspaces__err_401__unregistered_user(self):
45
+        self.testapp.authorization = (
46
+            'Basic',
47
+            (
48
+                'john@doe.doe',
49
+                'lapin'
50
+            )
51
+        )
52
+        res = self.testapp.post_json('/api/v2/users/1/workspaces', status=401)
53
+        assert isinstance(res.json, dict)
54
+        assert 'code' in res.json.keys()
55
+        assert 'message' in res.json.keys()
56
+        assert 'details' in res.json.keys()
57
+
58
+    def test_api__get_user_workspaces__err_404__user_does_not_exist(self):
59
+        self.testapp.authorization = (
60
+            'Basic',
61
+            (
62
+                'admin@admin.admin',
63
+                'admin@admin.admin'
64
+            )
65
+        )
66
+        res = self.testapp.post_json('/api/v2/users/5/workspaces', status=404)
67
+        assert isinstance(res.json, dict)
68
+        assert 'code' in res.json.keys()
69
+        assert 'message' in res.json.keys()
70
+        assert 'details' in res.json.keys()

+ 147 - 0
tracim/tests/functional/test_workspaces.py View File

@@ -0,0 +1,147 @@
1
+# coding=utf-8
2
+
3
+from tracim.tests import FunctionalTest
4
+
5
+
6
+class TestWorkspaceEndpoint(FunctionalTest):
7
+
8
+    def test_api__get_workspace__ok_200__nominal_case(self):
9
+        self.testapp.authorization = (
10
+            'Basic',
11
+            (
12
+                'admin@admin.admin',
13
+                'admin@admin.admin'
14
+            )
15
+        )
16
+        workspace = self.testapp.post_json('/api/v2/workspaces/1', status=200)
17
+        assert workspace['id'] == 1
18
+        assert workspace['slug'] == 'w1'
19
+        assert workspace['label'] == 'w1'
20
+        assert workspace['description'] == 'Just another description'
21
+        assert len(workspace['sidebar_entries']) == 3  # TODO change this
22
+
23
+        sidebar_entry = workspace['sidebar_entries'][0]
24
+        assert sidebar_entry['slug'] == 'markdown-pages'
25
+        assert sidebar_entry['label'] == 'Document Markdown'
26
+        assert sidebar_entry['route'] == "/#/workspace/{workspace_id}/contents/?type=mardown-page"  # nopep8
27
+        assert sidebar_entry['hexcolor'] == "#F0F9DC"
28
+        assert sidebar_entry['icon'] == "file-text-o"
29
+        # TODO To this for the other
30
+
31
+    def test_api__get_workspace__err_403__unallowed_user(self):
32
+        self.testapp.authorization = (
33
+            'Basic',
34
+            (
35
+                'lawrence-not-real-email@fsf.local',
36
+                'foobarbaz'
37
+            )
38
+        )
39
+        res = self.testapp.post_json('/api/v2/workspaces/1', status=403)
40
+        assert isinstance(res.json, dict)
41
+        assert 'code' in res.json.keys()
42
+        assert 'message' in res.json.keys()
43
+        assert 'details' in res.json.keys()
44
+
45
+    def test_api__get_workspace__err_401__unregistered_user(self):
46
+        self.testapp.authorization = (
47
+            'Basic',
48
+            (
49
+                'john@doe.doe',
50
+                'lapin'
51
+            )
52
+        )
53
+        res = self.testapp.post_json('/api/v2/workspaces/1', status=401)
54
+        assert isinstance(res.json, dict)
55
+        assert 'code' in res.json.keys()
56
+        assert 'message' in res.json.keys()
57
+        assert 'details' in res.json.keys()
58
+
59
+    def test_api__get_workspace__err_404__workspace_does_not_exist(self):
60
+        self.testapp.authorization = (
61
+            'Basic',
62
+            (
63
+                'admin@admin.admin',
64
+                'admin@admin.admin'
65
+            )
66
+        )
67
+        res = self.testapp.post_json('/api/v2/workspaces/5', status=404)
68
+        assert isinstance(res.json, dict)
69
+        assert 'code' in res.json.keys()
70
+        assert 'message' in res.json.keys()
71
+        assert 'details' in res.json.keys()
72
+
73
+
74
+class TestWorkspaceMembersEndpoint(FunctionalTest):
75
+
76
+    def test_api__get_workspace_members__ok_200__nominal_case(self):
77
+        self.testapp.authorization = (
78
+            'Basic',
79
+            (
80
+                'admin@admin.admin',
81
+                'admin@admin.admin'
82
+            )
83
+        )
84
+        res = self.testapp.post_json('/api/v2/workspaces/1/members', status=200)
85
+        assert len(res) == 2
86
+        user_role = res[0]
87
+        assert user_role['role'] == 'administrator'
88
+        assert user_role['user_id'] == '1'
89
+        assert user_role['workspace_id'] == '1'
90
+        assert user_role['user']['label'] == 'Global manager'
91
+        assert user_role['user']['avatar_url'] == ''  # TODO
92
+
93
+        assert res['role'] == 1
94
+        assert res['slug'] == 'w1'
95
+        assert res['label'] == 'w1'
96
+        assert res['description'] == 'Just another description'
97
+        assert len(res['sidebar_entries']) == 3  # TODO change this
98
+
99
+        sidebar_entry = res['sidebar_entries'][0]
100
+        assert sidebar_entry['slug'] == 'markdown-pages'
101
+        assert sidebar_entry['label'] == 'Document Markdown'
102
+        assert sidebar_entry['route'] == "/#/workspace/{workspace_id}/contents/?type=mardown-page"  # nopep8
103
+        assert sidebar_entry['hexcolor'] == "#F0F9DC"
104
+        assert sidebar_entry['icon'] == "file-text-o"
105
+        # TODO Do this for the other
106
+
107
+    def test_api__get_workspace_members__err_400__unallowed_user(self):
108
+        self.testapp.authorization = (
109
+            'Basic',
110
+            (
111
+                'lawrence-not-real-email@fsf.local',
112
+                'foobarbaz'
113
+            )
114
+        )
115
+        res = self.testapp.post_json('/api/v2/workspaces/1/members', status=403)
116
+        assert isinstance(res.json, dict)
117
+        assert 'code' in res.json.keys()
118
+        assert 'message' in res.json.keys()
119
+        assert 'details' in res.json.keys()
120
+
121
+    def test_api__get_workspace_members__err_401__unregistered_user(self):
122
+        self.testapp.authorization = (
123
+            'Basic',
124
+            (
125
+                'john@doe.doe',
126
+                'lapin'
127
+            )
128
+        )
129
+        res = self.testapp.post_json('/api/v2/workspaces/1/members', status=403)
130
+        assert isinstance(res.json, dict)
131
+        assert 'code' in res.json.keys()
132
+        assert 'message' in res.json.keys()
133
+        assert 'details' in res.json.keys()
134
+
135
+    def test_api__get_workspace_members__err_404__workspace_does_not_exist(self):
136
+        self.testapp.authorization = (
137
+            'Basic',
138
+            (
139
+                'admin@admin.admin',
140
+                'admin@admin.admin'
141
+            )
142
+        )
143
+        res = self.testapp.post_json('/api/v2/workspaces/5/members', status=404)
144
+        assert isinstance(res.json, dict)
145
+        assert 'code' in res.json.keys()
146
+        assert 'message' in res.json.keys()
147
+        assert 'details' in res.json.keys()

+ 52 - 1
tracim/views/core_api/schemas.py View File

@@ -2,7 +2,7 @@
2 2
 import marshmallow
3 3
 from marshmallow import post_load
4 4
 
5
-from tracim.models.context_models import LoginCredentials, UserInContext
5
+from tracim.models.context_models import LoginCredentials
6 6
 
7 7
 
8 8
 class ProfileSchema(marshmallow.Schema):
@@ -48,3 +48,54 @@ class LoginOutputHeaders(marshmallow.Schema):
48 48
 
49 49
 class NoContentSchema(marshmallow.Schema):
50 50
     pass
51
+
52
+
53
+class WorkspaceMenuEntrySchema(marshmallow.Schema):
54
+    slug = marshmallow.fields.String()
55
+    label = marshmallow.fields.String()
56
+    route = marshmallow.fields.String()
57
+    hexcolor = marshmallow.fields.String()
58
+    icon = marshmallow.fields.String()
59
+
60
+
61
+class WorkspaceSchema(marshmallow.Schema):
62
+    id = marshmallow.fields.Int()
63
+    slug = marshmallow.fields.String()
64
+    label = marshmallow.fields.String()
65
+    description = marshmallow.fields.String()
66
+    sidebar_entries = marshmallow.fields.Nested(
67
+        WorkspaceMenuEntrySchema,
68
+        many=True,
69
+    )
70
+
71
+
72
+class WorkspaceDigestSchema(marshmallow.Schema):
73
+    id = marshmallow.fields.Int()
74
+    label = marshmallow.fields.String()
75
+    sidebar_entries = marshmallow.fields.Nested(
76
+        WorkspaceMenuEntrySchema,
77
+        many=True,
78
+    )
79
+
80
+
81
+class WorkspaceMemberSchema(marshmallow.Schema):
82
+    role = marshmallow.fields.String()
83
+    user_id = marshmallow.fields.Int()
84
+    workspace_id = marshmallow.fields.Int()
85
+    # TODO user
86
+
87
+
88
+class ApplicationConfigSchema(marshmallow.Schema):
89
+    pass
90
+    #  TODO
91
+
92
+
93
+class ApplicationSchema(marshmallow.Schema):
94
+    label = marshmallow.fields.String()
95
+    slug = marshmallow.fields.String()
96
+    icon = marshmallow.fields.String()
97
+    hexcolor = marshmallow.fields.String()
98
+    is_active = marshmallow.fields.Boolean()
99
+    config = marshmallow.fields.Nested(
100
+        ApplicationConfigSchema,
101
+    )

+ 36 - 0
tracim/views/core_api/system_controller.py View File

@@ -0,0 +1,36 @@
1
+# coding=utf-8
2
+from pyramid.config import Configurator
3
+
4
+from tracim.models.applications import applications
5
+
6
+try:  # Python 3.5+
7
+    from http import HTTPStatus
8
+except ImportError:
9
+    from http import client as HTTPStatus
10
+
11
+from tracim import TracimRequest
12
+from tracim.extensions import hapic
13
+from tracim.views.controllers import Controller
14
+from tracim.views.core_api.schemas import ApplicationSchema
15
+
16
+
17
+class SystemController(Controller):
18
+
19
+    @hapic.with_api_doc()
20
+    @hapic.output_body(ApplicationSchema(many=True),)
21
+    def applications(self, context, request: TracimRequest, hapic_data=None):
22
+        """
23
+        Get list of alls applications installed in this tracim instance.
24
+        """
25
+        return applications
26
+
27
+    def bind(self, configurator: Configurator) -> None:
28
+        """
29
+        Create all routes and views using pyramid configurator
30
+        for this controller
31
+        """
32
+
33
+        # Applications
34
+        configurator.add_route('applications', '/system/applications', request_method='GET')  # nopep8
35
+        configurator.add_view(self.applications, route_name='applications')
36
+