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,8 +5,9 @@ class Fixture(object):
5 5
     """ Fixture classes (list) required for this fixtures"""
6 6
     require = NotImplemented
7 7
 
8
-    def __init__(self, session):
8
+    def __init__(self, session, config):
9 9
         self._session = session
10
+        self._config = config
10 11
 
11 12
     def insert(self):
12 13
         raise NotImplementedError()
@@ -17,10 +18,11 @@ class FixturesLoader(object):
17 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 22
         loaded = [] if loaded is None else loaded
22 23
         self._loaded = loaded
23 24
         self._session = session
25
+        self._config = config
24 26
 
25 27
     def loads(self, fixtures_classes):
26 28
         for fixture_class in fixtures_classes:
@@ -30,7 +32,10 @@ class FixturesLoader(object):
30 32
 
31 33
     def _load(self, fixture_class: Fixture):
32 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 39
             fixture.insert()
35 40
             self._loaded.append(fixture_class)
36 41
             self._session.flush()

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

@@ -1,7 +1,7 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 from tracim import models
3 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 7
 class Base(Fixture):
@@ -13,8 +13,11 @@ class Base(Fixture):
13 13
         u.email = 'admin@admin.admin'
14 14
         u.password = 'admin@admin.admin'
15 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 22
         g1 = models.Group()
20 23
         g1.group_id = 1

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


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

@@ -0,0 +1,168 @@
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,9 +7,9 @@ from .meta import DeclarativeBase
7 7
 from .revision_protection import prevent_content_revision_delete
8 8
 # import or define all models here to ensure they are attached to the
9 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 14
 # run configure_mappers after defining all of the models to ensure
15 15
 # all relationships can be setup
@@ -30,13 +30,13 @@ def get_tm_session(session_factory, transaction_manager):
30 30
     """
31 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 34
     will take care of committing any changes.
35 35
 
36 36
     - When using pyramid_tm it will automatically be committed or aborted
37 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 40
       For example::
41 41
 
42 42
           import transaction

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

@@ -4,6 +4,7 @@ import datetime as datetime_root
4 4
 import json
5 5
 import os
6 6
 from datetime import datetime
7
+from typing import TYPE_CHECKING
7 8
 
8 9
 from babel.dates import format_timedelta
9 10
 from bs4 import BeautifulSoup
@@ -690,6 +691,8 @@ class ContentRevisionRO(DeclarativeBase):
690 691
         if key in ('_sa_instance_state', ):  # Prevent infinite loop from SQLAlchemy code and altered set
691 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 696
         if inspect(self).has_identity \
694 697
                 and key in self._cloned_columns \
695 698
                 and not RevisionsIntegrity.is_updatable(self):
@@ -710,7 +713,7 @@ class ContentRevisionRO(DeclarativeBase):
710 713
 
711 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 717
         :return: bool, True if there is new information for given user else False
715 718
                        False if the user is None
716 719
         """
@@ -1203,7 +1206,7 @@ class Content(DeclarativeBase):
1203 1206
 
1204 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 1210
         :return: bool, True if there is new information for given user else False
1208 1211
                        False if the user is None
1209 1212
         """

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

@@ -8,9 +8,9 @@ from tracim.exceptions import ContentRevisionDeleteError
8 8
 from tracim.exceptions import ContentRevisionUpdateError
9 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 16
 def prevent_content_revision_delete(
@@ -71,7 +71,7 @@ def new_revision(
71 71
     """
72 72
     Prepare context to update a Content. It will add a new updatable revision
73 73
     to the content.
74
-    :param dbsession: Database session
74
+    :param dbsession: Database _session
75 75
     :param tm: TransactionManager
76 76
     :param content: Content instance to update
77 77
     :param force_create_new_revision: Decide if new_rev should or should not
@@ -87,7 +87,7 @@ def new_revision(
87 87
             yield content
88 88
         except SameValueError or ValueError as e:
89 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 91
             # to be add when problem happen.
92 92
             tm.abort()
93 93
             tm.begin()

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

@@ -7,7 +7,7 @@ from tracim.models.data import Content
7 7
 from tracim.logger import logger
8 8
 from tracim.fixtures import FixturesLoader
9 9
 from tracim.fixtures.users_and_groups import Base as BaseFixture
10
-
10
+from tracim.config import CFG
11 11
 
12 12
 class BaseTest(unittest.TestCase):
13 13
     """
@@ -16,7 +16,12 @@ class BaseTest(unittest.TestCase):
16 16
     def setUp(self):
17 17
         logger.debug(self, 'Setup Test...')
18 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 26
         self.config.include('tracim.models')
22 27
         settings = self.config.get_settings()
@@ -55,8 +60,8 @@ class BaseTest(unittest.TestCase):
55 60
 #         """
56 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 66
 #     def _create_content_and_test(self, name, workspace, *args, **kwargs) -> Content:
62 67
 #         """
@@ -66,20 +71,22 @@ class BaseTest(unittest.TestCase):
66 71
 #         content = Content(*args, **kwargs)
67 72
 #         content.label = name
68 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 77
 #         eq_(1, ContentApi.get_canonical_query().filter(Content.label == name).count())
73 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,6 +6,7 @@ from nose.tools import ok_
6 6
 from nose.tools import raises
7 7
 from sqlalchemy.sql.elements import and_
8 8
 from sqlalchemy.testing import eq_
9
+import transaction
9 10
 
10 11
 # from tracim.lib.content import ContentApi
11 12
 from tracim.exceptions import ContentRevisionUpdateError
@@ -16,10 +17,10 @@ from tracim.models.data import ActionDescription
16 17
 from tracim.models.data import ContentRevisionRO
17 18
 from tracim.models.data import ContentType
18 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 25
     @raises(ContentRevisionUpdateError)
25 26
     def test_update_without_prepare(self):
@@ -74,7 +75,11 @@ class TestContent(BaseTest):
74 75
         content = self.session.query(Content).filter(Content.id == created_content.id).one()
75 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 83
             time.sleep(0.00001)
79 84
             content.description = 'TEST_CONTENT_DESCRIPTION_1_UPDATED'
80 85
         self.session.flush()
@@ -82,7 +87,11 @@ class TestContent(BaseTest):
82 87
         eq_(2, self.session.query(ContentRevisionRO).filter(ContentRevisionRO.label == 'TEST_CONTENT_1').count())
83 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 95
             time.sleep(0.00001)
87 96
             content.description = 'TEST_CONTENT_DESCRIPTION_1_UPDATED_2'
88 97
             content.label = 'TEST_CONTENT_1_UPDATED_2'

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

@@ -38,7 +38,7 @@ class TestContentRevision(BaseTest):
38 38
 
39 39
     # TODO - G.M - 28-03-2018 - Reenable this test
40 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 42
     #     workspace = self._create_workspace_and_test(name='workspace_1', user=admin)
43 43
     #     folder = self._create_content_and_test(name='folder_1', workspace=workspace, type=ContentType.Folder)
44 44
     #     page = self._create_content_and_test(
@@ -50,7 +50,7 @@ class TestContentRevision(BaseTest):
50 50
     #         owner=admin
51 51
     #     )
52 52
     #
53
-    #     self.session.flush()
53
+    #     self._session.flush()
54 54
     #
55 55
     #     # Model create a new instance with list of column
56 56
     #     new_revision_by_model = ContentRevisionRO.new_from(page.revision)