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 json_fix # noqa: F401

9 import threadsafevariable

10 from py4web.core import ICECUBE

11 from py4web.core import Fixture as _Fixture

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

13

14 from .core import TypeDAL, TypedField, TypedTable

15 from .fields import PasswordField

16 from .types import Validator

17

18

19 class Fixture(_Fixture): # type: ignore

20 """

21 Make mypy happy.

22 """

23

24

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

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