Browse Source

add UserAPI and fix most of the model tests

Guénaël Muller 7 years ago
parent
commit
841d6bfff4

+ 8 - 3
tracim/fixtures/__init__.py View File

5
     """ Fixture classes (list) required for this fixtures"""
5
     """ Fixture classes (list) required for this fixtures"""
6
     require = NotImplemented
6
     require = NotImplemented
7
 
7
 
8
-    def __init__(self, session):
8
+    def __init__(self, session, config):
9
         self._session = session
9
         self._session = session
10
+        self._config = config
10
 
11
 
11
     def insert(self):
12
     def insert(self):
12
         raise NotImplementedError()
13
         raise NotImplementedError()
17
     Fixtures loader. Load each fixture once.
18
     Fixtures loader. Load each fixture once.
18
     """
19
     """
19
 
20
 
20
-    def __init__(self, session, loaded=None):
21
+    def __init__(self, session, config, loaded=None):
21
         loaded = [] if loaded is None else loaded
22
         loaded = [] if loaded is None else loaded
22
         self._loaded = loaded
23
         self._loaded = loaded
23
         self._session = session
24
         self._session = session
25
+        self._config = config
24
 
26
 
25
     def loads(self, fixtures_classes):
27
     def loads(self, fixtures_classes):
26
         for fixture_class in fixtures_classes:
28
         for fixture_class in fixtures_classes:
30
 
32
 
31
     def _load(self, fixture_class: Fixture):
33
     def _load(self, fixture_class: Fixture):
32
         if fixture_class not in self._loaded:
34
         if fixture_class not in self._loaded:
33
-            fixture = fixture_class(self._session)
35
+            fixture = fixture_class(
36
+                session=self._session,
37
+                config=self._config,
38
+            )
34
             fixture.insert()
39
             fixture.insert()
35
             self._loaded.append(fixture_class)
40
             self._loaded.append(fixture_class)
36
             self._session.flush()
41
             self._session.flush()

+ 6 - 3
tracim/fixtures/users_and_groups.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 from tracim import models
2
 from tracim import models
3
 from tracim.fixtures import Fixture
3
 from tracim.fixtures import Fixture
4
-#from tracim.lib.user import UserApi
4
+from tracim.lib.user import UserApi
5
 
5
 
6
 
6
 
7
 class Base(Fixture):
7
 class Base(Fixture):
13
         u.email = 'admin@admin.admin'
13
         u.email = 'admin@admin.admin'
14
         u.password = 'admin@admin.admin'
14
         u.password = 'admin@admin.admin'
15
         self._session.add(u)
15
         self._session.add(u)
16
-        #uapi = UserApi(u)
17
-        #uapi.execute_created_user_actions(u)
16
+        uapi = UserApi(
17
+            session=self._session,
18
+            config=self._config,
19
+            current_user=u)
20
+        uapi.execute_created_user_actions(u)
18
 
21
 
19
         g1 = models.Group()
22
         g1 = models.Group()
20
         g1.group_id = 1
23
         g1.group_id = 1

+ 0 - 0
tracim/lib/__init__.py View File


+ 168 - 0
tracim/lib/user.py View File

1
+# -*- coding: utf-8 -*-
2
+import threading
3
+
4
+import transaction
5
+import typing as typing
6
+
7
+from tracim.models.auth import User
8
+
9
+# TODO - G.M -28-03-2018 - Check if "current user" stuff is always needed for tracimv2
10
+# CURRENT_USER_WEB = 'WEB'
11
+# CURRENT_USER_WSGIDAV = 'WSGIDAV'
12
+
13
+
14
+class UserApi(object):
15
+
16
+    def __init__(self, current_user: User, session, config):
17
+        self._session = session
18
+        self._user = current_user
19
+        self._config = config
20
+
21
+    def get_all(self):
22
+        return self._session.query(User).order_by(User.display_name).all()
23
+
24
+    def _base_query(self):
25
+        return self._session.query(User)
26
+
27
+    def get_one(self, user_id: int):
28
+        return self._base_query().filter(User.user_id==user_id).one()
29
+
30
+    def get_one_by_email(self, email: str):
31
+        return self._base_query().filter(User.email==email).one()
32
+
33
+    def get_one_by_id(self, id: int) -> User:
34
+        return self._base_query().filter(User.user_id==id).one()
35
+
36
+    def update(
37
+            self,
38
+            user: User,
39
+            name: str=None,
40
+            email: str=None,
41
+            do_save=True,
42
+            timezone: str='',
43
+    ):
44
+        if name is not None:
45
+            user.display_name = name
46
+
47
+        if email is not None:
48
+            user.email = email
49
+
50
+        user.timezone = timezone
51
+
52
+        if do_save:
53
+            self.save(user)
54
+
55
+        if email and self._user and user.user_id==self._user.user_id:
56
+            pass
57
+            # this is required for the _session to keep on being up-to-date
58
+            # TODO - G.M - 28-03-2018 - Check for pyramid equivalent
59
+            # tg.request.identity['repoze.who.userid'] = email
60
+            # tg.auth_force_login(email)
61
+
62
+    def user_with_email_exists(self, email: str):
63
+        try:
64
+            self.get_one_by_email(email)
65
+            return True
66
+        except:
67
+            return False
68
+
69
+    def create_user(self, email=None, groups=[], save_now=False) -> User:
70
+        user = User()
71
+
72
+        if email:
73
+            user.email = email
74
+
75
+        for group in groups:
76
+            user.groups.append(group)
77
+
78
+        self._session.add(user)
79
+
80
+        if save_now:
81
+            self._session.flush()
82
+
83
+        return user
84
+
85
+    def save(self, user: User):
86
+        self._session.flush()
87
+
88
+    def execute_created_user_actions(self, created_user: User) -> None:
89
+        """
90
+        Execute actions when user just been created
91
+        :return:
92
+        """
93
+        # NOTE: Cyclic import
94
+        # TODO - G.M - 28-03-2018 - Reenable Calendar stuff
95
+        #from tracim.lib.calendar import CalendarManager
96
+        #from tracim.model.organisational import UserCalendar
97
+
98
+        created_user.ensure_auth_token(dbsession=self._session, validity_seconds=self._config.USER_AUTH_TOKEN_VALIDITY)
99
+
100
+        # Ensure database is up-to-date
101
+        self._session.flush()
102
+        transaction.commit()
103
+
104
+        # calendar_manager = CalendarManager(created_user)
105
+        # calendar_manager.create_then_remove_fake_event(
106
+        #     calendar_class=UserCalendar,
107
+        #     related_object_id=created_user.user_id,
108
+        # )
109
+
110
+
111
+# class CurrentUserGetterInterface(object):
112
+#     def get_current_user(self) -> typing.Union[None, User]:
113
+#         raise NotImplementedError()
114
+#
115
+#
116
+# class BaseCurrentUserGetter(CurrentUserGetterInterface):
117
+#     def __init__(self) -> None:
118
+#         self.api = UserApi(None)
119
+
120
+# TODO - G.M - 28-03-2018 - Check for pyramid equivalent
121
+# class WebCurrentUserGetter(BaseCurrentUserGetter):
122
+#     def get_current_user(self) -> typing.Union[None, User]:
123
+#         # HACK - D.A. - 2015-09-02
124
+#         # In tests, the tg.request.identity may not be set
125
+#         # (this is a buggy case, but for now this is how the software is;)
126
+#         if tg.request is not None:
127
+#             if hasattr(tg.request, 'identity'):
128
+#                 if tg.request.identity is not None:
129
+#                     return self.api.get_one_by_email(
130
+#                         tg.request.identity['repoze.who.userid'],
131
+#                     )
132
+#
133
+#         return None
134
+
135
+# TODO - G.M - 28-03-2018 - Reenable Webdav stuff
136
+# class WsgidavCurrentUserGetter(BaseCurrentUserGetter):
137
+#     def get_current_user(self) -> typing.Union[None, User]:
138
+#         if hasattr(cherrypy.request, 'current_user_email'):
139
+#             return self.api.get_one_by_email(
140
+#                 cherrypy.request.current_user_email,
141
+#             )
142
+#
143
+#         return None
144
+
145
+
146
+# class CurrentUserGetterApi(object):
147
+#     thread_local = threading.local()
148
+#     matches = {
149
+#         CURRENT_USER_WEB: WebCurrentUserGetter,
150
+#         CURRENT_USER_WSGIDAV: WsgidavCurrentUserGetter,
151
+#     }
152
+#     default = CURRENT_USER_WEB
153
+#
154
+#     @classmethod
155
+#     def get_current_user(cls) -> User:
156
+#         try:
157
+#             return cls.thread_local.getter.get_current_user()
158
+#         except AttributeError:
159
+#             return cls.factory(cls.default).get_current_user()
160
+#
161
+#     @classmethod
162
+#     def set_thread_local_getter(cls, name) -> None:
163
+#         if not hasattr(cls.thread_local, 'getter'):
164
+#             cls.thread_local.getter = cls.factory(name)
165
+#
166
+#     @classmethod
167
+#     def factory(cls, name: str) -> CurrentUserGetterInterface:
168
+#         return cls.matches[name]()

+ 5 - 5
tracim/models/__init__.py View File

7
 from .revision_protection import prevent_content_revision_delete
7
 from .revision_protection import prevent_content_revision_delete
8
 # import or define all models here to ensure they are attached to the
8
 # import or define all models here to ensure they are attached to the
9
 # Base.metadata prior to any initialization routines
9
 # Base.metadata prior to any initialization routines
10
-from .mymodel import MyModel  # flake8: noqa
11
-from .auth import User, Group, Permission
12
-from .data import Content, ContentRevisionRO
10
+from tracim.models.mymodel import MyModel  # flake8: noqa
11
+from tracim.models.auth import User, Group, Permission
12
+from tracim.models.data import Content, ContentRevisionRO
13
 
13
 
14
 # run configure_mappers after defining all of the models to ensure
14
 # run configure_mappers after defining all of the models to ensure
15
 # all relationships can be setup
15
 # all relationships can be setup
30
     """
30
     """
31
     Get a ``sqlalchemy.orm.Session`` instance backed by a transaction.
31
     Get a ``sqlalchemy.orm.Session`` instance backed by a transaction.
32
 
32
 
33
-    This function will hook the session to the transaction manager which
33
+    This function will hook the _session to the transaction manager which
34
     will take care of committing any changes.
34
     will take care of committing any changes.
35
 
35
 
36
     - When using pyramid_tm it will automatically be committed or aborted
36
     - When using pyramid_tm it will automatically be committed or aborted
37
       depending on whether an exception is raised.
37
       depending on whether an exception is raised.
38
 
38
 
39
-    - When using scripts you should wrap the session in a manager yourself.
39
+    - When using scripts you should wrap the _session in a manager yourself.
40
       For example::
40
       For example::
41
 
41
 
42
           import transaction
42
           import transaction

+ 5 - 2
tracim/models/data.py View File

4
 import json
4
 import json
5
 import os
5
 import os
6
 from datetime import datetime
6
 from datetime import datetime
7
+from typing import TYPE_CHECKING
7
 
8
 
8
 from babel.dates import format_timedelta
9
 from babel.dates import format_timedelta
9
 from bs4 import BeautifulSoup
10
 from bs4 import BeautifulSoup
690
         if key in ('_sa_instance_state', ):  # Prevent infinite loop from SQLAlchemy code and altered set
691
         if key in ('_sa_instance_state', ):  # Prevent infinite loop from SQLAlchemy code and altered set
691
             return super().__setattr__(key, value)
692
             return super().__setattr__(key, value)
692
 
693
 
694
+        # FIXME - G.M - 28-03-2018 - Cycling Import
695
+        from tracim.models.revision_protection import RevisionsIntegrity
693
         if inspect(self).has_identity \
696
         if inspect(self).has_identity \
694
                 and key in self._cloned_columns \
697
                 and key in self._cloned_columns \
695
                 and not RevisionsIntegrity.is_updatable(self):
698
                 and not RevisionsIntegrity.is_updatable(self):
710
 
713
 
711
     def has_new_information_for(self, user: User) -> bool:
714
     def has_new_information_for(self, user: User) -> bool:
712
         """
715
         """
713
-        :param user: the session current user
716
+        :param user: the _session current user
714
         :return: bool, True if there is new information for given user else False
717
         :return: bool, True if there is new information for given user else False
715
                        False if the user is None
718
                        False if the user is None
716
         """
719
         """
1203
 
1206
 
1204
     def has_new_information_for(self, user: User) -> bool:
1207
     def has_new_information_for(self, user: User) -> bool:
1205
         """
1208
         """
1206
-        :param user: the session current user
1209
+        :param user: the _session current user
1207
         :return: bool, True if there is new information for given user else False
1210
         :return: bool, True if there is new information for given user else False
1208
                        False if the user is None
1211
                        False if the user is None
1209
         """
1212
         """

+ 5 - 5
tracim/models/revision_protection.py View File

8
 from tracim.exceptions import ContentRevisionUpdateError
8
 from tracim.exceptions import ContentRevisionUpdateError
9
 from tracim.exceptions import SameValueError
9
 from tracim.exceptions import SameValueError
10
 
10
 
11
-from .data import ContentRevisionRO
12
-from .data import Content
13
-from .meta import DeclarativeBase
11
+from tracim.models.data import ContentRevisionRO
12
+from tracim.models.data import Content
13
+from tracim.models.meta import DeclarativeBase
14
 
14
 
15
 
15
 
16
 def prevent_content_revision_delete(
16
 def prevent_content_revision_delete(
71
     """
71
     """
72
     Prepare context to update a Content. It will add a new updatable revision
72
     Prepare context to update a Content. It will add a new updatable revision
73
     to the content.
73
     to the content.
74
-    :param dbsession: Database session
74
+    :param dbsession: Database _session
75
     :param tm: TransactionManager
75
     :param tm: TransactionManager
76
     :param content: Content instance to update
76
     :param content: Content instance to update
77
     :param force_create_new_revision: Decide if new_rev should or should not
77
     :param force_create_new_revision: Decide if new_rev should or should not
87
             yield content
87
             yield content
88
         except SameValueError or ValueError as e:
88
         except SameValueError or ValueError as e:
89
             # INFO - 20-03-2018 - renew transaction when error happened
89
             # INFO - 20-03-2018 - renew transaction when error happened
90
-            # This avoid bad session data like new "temporary" revision
90
+            # This avoid bad _session data like new "temporary" revision
91
             # to be add when problem happen.
91
             # to be add when problem happen.
92
             tm.abort()
92
             tm.abort()
93
             tm.begin()
93
             tm.begin()

+ 25 - 18
tracim/tests/__init__.py View File

7
 from tracim.logger import logger
7
 from tracim.logger import logger
8
 from tracim.fixtures import FixturesLoader
8
 from tracim.fixtures import FixturesLoader
9
 from tracim.fixtures.users_and_groups import Base as BaseFixture
9
 from tracim.fixtures.users_and_groups import Base as BaseFixture
10
-
10
+from tracim.config import CFG
11
 
11
 
12
 class BaseTest(unittest.TestCase):
12
 class BaseTest(unittest.TestCase):
13
     """
13
     """
16
     def setUp(self):
16
     def setUp(self):
17
         logger.debug(self, 'Setup Test...')
17
         logger.debug(self, 'Setup Test...')
18
         self.config = testing.setUp(settings={
18
         self.config = testing.setUp(settings={
19
-            'sqlalchemy.url': 'sqlite:///:memory:'
19
+            'sqlalchemy.url': 'sqlite:///:memory:',
20
+            'user.auth_token.validity':'604800',
21
+            'depot_storage_dir': '/tmp/test/depot',
22
+            'depot_storage_name': 'test',
23
+            'preview_cache_dir': '/tmp/test/preview_cache',
24
+
20
         })
25
         })
21
         self.config.include('tracim.models')
26
         self.config.include('tracim.models')
22
         settings = self.config.get_settings()
27
         settings = self.config.get_settings()
55
 #         """
60
 #         """
56
 #         WorkspaceApi(user).create_workspace(name, save_now=True)
61
 #         WorkspaceApi(user).create_workspace(name, save_now=True)
57
 #
62
 #
58
-#         eq_(1, self.session.query(Workspace).filter(Workspace.label == name).count())
59
-#         return self.session.query(Workspace).filter(Workspace.label == name).one()
63
+#         eq_(1, self._session.query(Workspace).filter(Workspace.label == name).count())
64
+#         return self._session.query(Workspace).filter(Workspace.label == name).one()
60
 #
65
 #
61
 #     def _create_content_and_test(self, name, workspace, *args, **kwargs) -> Content:
66
 #     def _create_content_and_test(self, name, workspace, *args, **kwargs) -> Content:
62
 #         """
67
 #         """
66
 #         content = Content(*args, **kwargs)
71
 #         content = Content(*args, **kwargs)
67
 #         content.label = name
72
 #         content.label = name
68
 #         content.workspace = workspace
73
 #         content.workspace = workspace
69
-#         self.session.add(content)
70
-#         self.session.flush()
74
+#         self._session.add(content)
75
+#         self._session.flush()
71
 #
76
 #
72
 #         eq_(1, ContentApi.get_canonical_query().filter(Content.label == name).count())
77
 #         eq_(1, ContentApi.get_canonical_query().filter(Content.label == name).count())
73
 #         return ContentApi.get_canonical_query().filter(Content.label == name).one()
78
 #         return ContentApi.get_canonical_query().filter(Content.label == name).one()
74
-#
75
-#
76
-# class StandardTest(BaseTest):
77
-#     """
78
-#     BaseTest with default fixtures
79
-#     """
80
-#     fixtures = [BaseFixture]
81
-#
82
-#     def init_database(self):
83
-#         BaseTest.init_database(self)
84
-#         fixtures_loader = FixturesLoader([BaseFixture])
85
-#         fixtures_loader.loads(self.fixtures)
79
+
80
+
81
+class StandardTest(BaseTest):
82
+    """
83
+    BaseTest with default fixtures
84
+    """
85
+    fixtures = [BaseFixture]
86
+
87
+    def init_database(self):
88
+        BaseTest.init_database(self)
89
+        fixtures_loader = FixturesLoader(
90
+            session=self.session,
91
+            config=CFG(self.config.get_settings()))
92
+        fixtures_loader.loads(self.fixtures)

+ 13 - 4
tracim/tests/models/test_content.py View File

6
 from nose.tools import raises
6
 from nose.tools import raises
7
 from sqlalchemy.sql.elements import and_
7
 from sqlalchemy.sql.elements import and_
8
 from sqlalchemy.testing import eq_
8
 from sqlalchemy.testing import eq_
9
+import transaction
9
 
10
 
10
 # from tracim.lib.content import ContentApi
11
 # from tracim.lib.content import ContentApi
11
 from tracim.exceptions import ContentRevisionUpdateError
12
 from tracim.exceptions import ContentRevisionUpdateError
16
 from tracim.models.data import ContentRevisionRO
17
 from tracim.models.data import ContentRevisionRO
17
 from tracim.models.data import ContentType
18
 from tracim.models.data import ContentType
18
 from tracim.models.data import Workspace
19
 from tracim.models.data import Workspace
19
-from tracim.tests import BaseTest
20
+from tracim.tests import StandardTest
20
 
21
 
21
 
22
 
22
-class TestContent(BaseTest):
23
+class TestContent(StandardTest):
23
 
24
 
24
     @raises(ContentRevisionUpdateError)
25
     @raises(ContentRevisionUpdateError)
25
     def test_update_without_prepare(self):
26
     def test_update_without_prepare(self):
74
         content = self.session.query(Content).filter(Content.id == created_content.id).one()
75
         content = self.session.query(Content).filter(Content.id == created_content.id).one()
75
         eq_(1, self.session.query(ContentRevisionRO).filter(ContentRevisionRO.label == 'TEST_CONTENT_1').count())
76
         eq_(1, self.session.query(ContentRevisionRO).filter(ContentRevisionRO.label == 'TEST_CONTENT_1').count())
76
 
77
 
77
-        with new_revision(content):
78
+        with new_revision(
79
+                dbsession=self.session,
80
+                tm=transaction.manager,
81
+                content=content
82
+        ):
78
             time.sleep(0.00001)
83
             time.sleep(0.00001)
79
             content.description = 'TEST_CONTENT_DESCRIPTION_1_UPDATED'
84
             content.description = 'TEST_CONTENT_DESCRIPTION_1_UPDATED'
80
         self.session.flush()
85
         self.session.flush()
82
         eq_(2, self.session.query(ContentRevisionRO).filter(ContentRevisionRO.label == 'TEST_CONTENT_1').count())
87
         eq_(2, self.session.query(ContentRevisionRO).filter(ContentRevisionRO.label == 'TEST_CONTENT_1').count())
83
         eq_(1, self.session.query(Content).filter(Content.id == created_content.id).count())
88
         eq_(1, self.session.query(Content).filter(Content.id == created_content.id).count())
84
 
89
 
85
-        with new_revision(content):
90
+        with new_revision(
91
+                dbsession=self.session,
92
+                tm=transaction.manager,
93
+                content=content
94
+        ):
86
             time.sleep(0.00001)
95
             time.sleep(0.00001)
87
             content.description = 'TEST_CONTENT_DESCRIPTION_1_UPDATED_2'
96
             content.description = 'TEST_CONTENT_DESCRIPTION_1_UPDATED_2'
88
             content.label = 'TEST_CONTENT_1_UPDATED_2'
97
             content.label = 'TEST_CONTENT_1_UPDATED_2'

+ 2 - 2
tracim/tests/models/test_content_revision.py View File

38
 
38
 
39
     # TODO - G.M - 28-03-2018 - Reenable this test
39
     # TODO - G.M - 28-03-2018 - Reenable this test
40
     # def test_new_revision(self):
40
     # def test_new_revision(self):
41
-    #     admin = self.session.query(User).filter(User.email == 'admin@admin.admin').one()
41
+    #     admin = self._session.query(User).filter(User.email == 'admin@admin.admin').one()
42
     #     workspace = self._create_workspace_and_test(name='workspace_1', user=admin)
42
     #     workspace = self._create_workspace_and_test(name='workspace_1', user=admin)
43
     #     folder = self._create_content_and_test(name='folder_1', workspace=workspace, type=ContentType.Folder)
43
     #     folder = self._create_content_and_test(name='folder_1', workspace=workspace, type=ContentType.Folder)
44
     #     page = self._create_content_and_test(
44
     #     page = self._create_content_and_test(
50
     #         owner=admin
50
     #         owner=admin
51
     #     )
51
     #     )
52
     #
52
     #
53
-    #     self.session.flush()
53
+    #     self._session.flush()
54
     #
54
     #
55
     #     # Model create a new instance with list of column
55
     #     # Model create a new instance with list of column
56
     #     new_revision_by_model = ContentRevisionRO.new_from(page.revision)
56
     #     new_revision_by_model = ContentRevisionRO.new_from(page.revision)