Skip to content

Commit a4ba914

Browse files
authored
Merge pull request #14 from hussainwali74/schemas_improvement
refactor session schema
2 parents 2a47916 + 2a4e718 commit a4ba914

File tree

1 file changed

+64
-38
lines changed

1 file changed

+64
-38
lines changed

crudadmin/session/schemas.py

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,65 @@
11
from datetime import UTC, datetime
2+
from enum import Enum
23
from typing import Any, Optional
34
from uuid import uuid4
45

5-
from pydantic import BaseModel, Field
6+
from pydantic import BaseModel, Field, field_validator
67

78
from ..core.schemas.timestamp import TimestampSchema
89

910

10-
class SessionData(BaseModel):
11-
"""Common base data for any user session."""
11+
class DeviceType(str, Enum):
12+
"""Device type enumeration"""
1213

13-
user_id: int
14-
session_id: str = Field(default_factory=lambda: str(uuid4()))
15-
ip_address: str
16-
user_agent: str
17-
device_info: dict[str, Any] = Field(default_factory=dict)
18-
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
19-
last_activity: datetime = Field(default_factory=lambda: datetime.now(UTC))
20-
is_active: bool = True
21-
metadata: dict[str, Any] = Field(default_factory=dict)
14+
MOBILE = "mobile"
15+
TABLET = "tablet"
16+
DESKTOP = "desktop"
17+
UNKNOWN = "unknown"
18+
19+
20+
class BaseSession(BaseModel):
21+
"""Common base fields for all session types."""
22+
23+
user_id: int = Field(
24+
..., description="The ID of the user associated with the session."
25+
)
26+
27+
session_id: str = Field(
28+
default_factory=lambda: str(uuid4()), description="Unique session identifier"
29+
)
30+
ip_address: str = Field(..., description="Client IP address")
31+
user_agent: str = Field(..., description="Client user agent string")
32+
device_info: dict[str, Any] = Field(
33+
default_factory=dict, description="Additional device information"
34+
)
35+
is_active: bool = Field(default=True, description="Whether session is active")
36+
37+
@field_validator("ip_address")
38+
@classmethod
39+
def validate_ip_address(cls, ip: str) -> str:
40+
"""Validate IP address format."""
41+
import ipaddress
42+
43+
try:
44+
ipaddress.ip_address(ip)
45+
except ValueError as err:
46+
raise ValueError("Invalid IP Address format") from err
47+
return ip
48+
49+
50+
class SessionData(BaseSession):
51+
"""Common session data for any user session."""
52+
53+
metadata: dict[str, Any] = Field(
54+
default_factory=dict, description="Additional session metadata"
55+
)
56+
created_at: datetime = Field(
57+
default_factory=lambda: datetime.now(UTC),
58+
description="Session creation timestamp",
59+
)
60+
last_activity: datetime = Field(
61+
default_factory=lambda: datetime.now(UTC), description="Last activity timestamp"
62+
)
2263

2364

2465
class SessionCreate(SessionData):
@@ -38,9 +79,9 @@ class SessionUpdate(BaseModel):
3879
class UserAgentInfo(BaseModel):
3980
"""User agent information parsed from the User-Agent header."""
4081

41-
browser: str
42-
browser_version: str
43-
os: str
82+
browser: str = Field(..., description="Browser name")
83+
browser_version: str = Field(..., description="Browser version")
84+
os: str = Field(..., description="Operating System")
4485
device: str
4586
is_mobile: bool
4687
is_tablet: bool
@@ -57,46 +98,32 @@ class CSRFToken(BaseModel):
5798
expires_at: datetime
5899

59100

60-
class AdminSessionBase(BaseModel):
61-
"""Base schema for AdminSession."""
62-
63-
user_id: int
64-
session_id: str
65-
ip_address: str
66-
user_agent: str
67-
device_info: dict[str, Any] = Field(default_factory=dict)
68-
session_metadata: dict[str, Any] = Field(default_factory=dict)
69-
is_active: bool = True
70-
71-
72-
class AdminSession(TimestampSchema, AdminSessionBase):
101+
class AdminSession(TimestampSchema, BaseSession):
73102
"""Full AdminSession schema with all fields."""
74103

75104
id: int
76-
created_at: datetime
77-
last_activity: datetime
105+
session_metadata: dict[str, Any] = Field(
106+
default_factory=dict, description="admin specific session metadata"
107+
)
108+
# remove redundant timestamp fields - let TimestampSchema handle them
78109

79110

80-
class AdminSessionRead(BaseModel):
111+
class AdminSessionRead(BaseSession):
81112
"""Schema for reading AdminSession data."""
82113

83114
id: int
84-
user_id: int
85-
session_id: str
86-
ip_address: str
87-
user_agent: str
88-
device_info: dict[str, Any]
89115
session_metadata: dict[str, Any]
90116
created_at: datetime
91117
last_activity: datetime
92118
is_active: bool
93119

94120

95-
class AdminSessionCreate(AdminSessionBase):
121+
class AdminSessionCreate(BaseSession):
96122
"""Schema for creating AdminSession in database."""
97123

98124
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
99125
last_activity: datetime = Field(default_factory=lambda: datetime.now(UTC))
126+
session_metadata: dict[str, Any] = Field(default_factory=dict)
100127

101128

102129
class AdminSessionUpdate(BaseModel):
@@ -119,7 +146,6 @@ class AdminSessionUpdateInternal(AdminSessionUpdate):
119146
"SessionUpdate",
120147
"UserAgentInfo",
121148
"CSRFToken",
122-
"AdminSessionBase",
123149
"AdminSession",
124150
"AdminSessionRead",
125151
"AdminSessionCreate",

0 commit comments

Comments
 (0)