Coverage for src/typedal/for_py4web.py: 100%

26 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-19 14:35 +0100

1""" 

2ONLY USE IN COMBINATION WITH PY4WEB! 

3""" 

4import typing 

5from datetime import datetime 

6from typing import Any, Optional 

7 

8import threadsafevariable 

9from py4web.core import ICECUBE 

10from py4web.core import Fixture as _Fixture 

11from pydal.validators import CRYPT, IS_EMAIL, IS_NOT_EMPTY, IS_NOT_IN_DB, IS_STRONG 

12 

13from .core import TypeDAL, TypedField, TypedTable 

14from .fields import PasswordField 

15from .types import Validator 

16 

17 

18class Fixture(_Fixture): # type: ignore 

19 """ 

20 Make mypy happy. 

21 """ 

22 

23 

24class DAL(TypeDAL, Fixture): # pragma: no cover 

25 """ 

26 Fixture similar to the py4web pydal fixture, but for typedal. 

27 """ 

28 

29 def on_request(self, _: dict[str, Any]) -> None: 

30 """ 

31 Make sure there is a database connection when a request comes in. 

32 """ 

33 self.get_connection_from_pool_or_new() 

34 threadsafevariable.ThreadSafeVariable.restore(ICECUBE) 

35 

36 def on_error(self, _: dict[str, Any]) -> None: 

37 """ 

38 Rollback db on error. 

39 """ 

40 self.recycle_connection_in_pool_or_close("rollback") 

41 

42 def on_success(self, _: dict[str, Any]) -> None: 

43 """ 

44 Commit db on success. 

45 """ 

46 self.recycle_connection_in_pool_or_close("commit") 

47 

48 

49class AuthUser(TypedTable): 

50 """ 

51 Class for db.auth_user in py4web (probably not w2p). 

52 """ 

53 

54 # fixme: these settings are probably not passed anymore! 

55 redefine = True 

56 migrate = False 

57 # /fixme 

58 

59 # call db.define on this when ready 

60 

61 email: TypedField[str] 

62 password = PasswordField(requires=[IS_STRONG(entropy=45), CRYPT()]) 

63 first_name: TypedField[Optional[str]] 

64 last_name: TypedField[Optional[str]] 

65 sso_id: TypedField[Optional[str]] 

66 action_token: TypedField[Optional[str]] 

67 last_password_change: TypedField[Optional[datetime]] 

68 

69 # past_passwords_hash: Optional[str] 

70 # username: Optional[str] 

71 # phone_number: Optional[str] 

72 

73 @classmethod 

74 def __on_define__(cls, db: TypeDAL) -> None: 

75 """ 

76 Add some requires= to the auth_user fields. 

77 """ 

78 cls.email.requires = typing.cast(tuple[Validator, ...], (IS_EMAIL(), IS_NOT_IN_DB(db, "auth_user.email"))) 

79 cls.first_name.requires = IS_NOT_EMPTY() 

80 cls.last_name.requires = IS_NOT_EMPTY()