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

27 statements  

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

1""" 

2ONLY USE IN COMBINATION WITH PY4WEB! 

3""" 

4import typing 

5from datetime import datetime 

6from typing import Any, Optional 

7 

8import json_fix # noqa: F401 

9import threadsafevariable 

10from py4web.core import ICECUBE 

11from py4web.core import Fixture as _Fixture 

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

13 

14from .core import TypeDAL, TypedField, TypedTable 

15from .fields import PasswordField 

16from .types import Validator 

17 

18 

19class Fixture(_Fixture): # type: ignore 

20 """ 

21 Make mypy happy. 

22 """ 

23 

24 

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

26 """ 

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

28 """ 

29 

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

31 """ 

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

33 """ 

34 self.get_connection_from_pool_or_new() 

35 threadsafevariable.ThreadSafeVariable.restore(ICECUBE) 

36 

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

38 """ 

39 Rollback db on error. 

40 """ 

41 self.recycle_connection_in_pool_or_close("rollback") 

42 

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

44 """ 

45 Commit db on success. 

46 """ 

47 self.recycle_connection_in_pool_or_close("commit") 

48 

49 

50class AuthUser(TypedTable): 

51 """ 

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

53 """ 

54 

55 redefine = True 

56 migrate = False 

57 

58 # call db.define on this when ready 

59 

60 email: TypedField[str] 

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

62 first_name: TypedField[Optional[str]] 

63 last_name: TypedField[Optional[str]] 

64 sso_id: TypedField[Optional[str]] 

65 action_token: TypedField[Optional[str]] 

66 last_password_change: TypedField[Optional[datetime]] 

67 

68 # past_passwords_hash: Optional[str] 

69 # username: Optional[str] 

70 # phone_number: Optional[str] 

71 

72 @classmethod 

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

74 """ 

75 Add some requires= to the auth_user fields. 

76 """ 

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

78 cls.first_name.requires = IS_NOT_EMPTY() 

79 cls.last_name.requires = IS_NOT_EMPTY()