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

1"""

2 ONLY USE IN COMBINATION WITH PY4WEB!

3"""

4 import typing

5 from datetime import datetime

6 from typing import Any, Optional

7

8 import threadsafevariable

9 from py4web.core import ICECUBE

10 from py4web.core import Fixture as _Fixture

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

12

13 from .core import TypeDAL, TypedField, TypedTable

14 from .fields import PasswordField

15 from .types import Validator

16

17

18 class Fixture(_Fixture): # type: ignore

19 """

20 Make mypy happy.

21 """

22

23

24 class 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

49 class 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()