11"""Functionality for managing sessions."""
22
3- from dataclasses import dataclass
3+ from dataclasses import asdict , dataclass
44from datetime import UTC , datetime , timedelta
55from typing import Self
66from uuid import uuid4
1111from fmu_settings_api .config import settings
1212
1313
14+ class SessionNotFoundError (ValueError ):
15+ """Raised when getting a session id that does not exist."""
16+
17+
1418@dataclass
1519class Session :
1620 """Represents session information when working on an FMU Directory."""
1721
1822 id : str
19- fmu_directory : ProjectFMUDirectory
2023 user_fmu_directory : UserFMUDirectory
2124 created_at : datetime
2225 expires_at : datetime
2326 last_accessed : datetime
2427
2528
29+ @dataclass
30+ class ProjectSession (Session ):
31+ """A session with an FMU project attached."""
32+
33+ project_fmu_directory : ProjectFMUDirectory
34+
35+
2636class SessionManager :
2737 """Manages sessions started when an FMU Directory as been opened.
2838
@@ -31,7 +41,7 @@ class SessionManager:
3141 it simply uses a dictionary backend.
3242 """
3343
34- Storage = dict [str , Session ]
44+ Storage = dict [str , Session | ProjectSession ]
3545 """Type alias for the storage backend instance."""
3646
3747 storage : Storage
@@ -41,15 +51,21 @@ def __init__(self: Self) -> None:
4151 """Initializes the session manager singleton."""
4252 self .storage = {}
4353
44- async def _store_session (self : Self , session_id : str , session : Session ) -> None :
54+ async def _store_session (
55+ self : Self , session_id : str , session : Session | ProjectSession
56+ ) -> None :
4557 """Stores a newly created session."""
4658 self .storage [session_id ] = session
4759
48- async def _retrieve_session (self : Self , session_id : str ) -> Session | None :
60+ async def _retrieve_session (
61+ self : Self , session_id : str
62+ ) -> Session | ProjectSession | None :
4963 """Retrieves a session from the storage backend."""
5064 return self .storage .get (session_id , None )
5165
52- async def _update_session (self : Self , session_id : str , session : Session ) -> None :
66+ async def _update_session (
67+ self : Self , session_id : str , session : Session | ProjectSession
68+ ) -> None :
5369 """Stores an updated session back into the session backend."""
5470 self .storage [session_id ] = session
5571
@@ -61,7 +77,6 @@ async def destroy_session(self: Self, session_id: str) -> None:
6177
6278 async def create_session (
6379 self : Self ,
64- fmu_directory : ProjectFMUDirectory ,
6580 user_fmu_directory : UserFMUDirectory ,
6681 expire_seconds : int = settings .SESSION_EXPIRE_SECONDS ,
6782 ) -> str :
@@ -72,7 +87,6 @@ async def create_session(
7287
7388 session = Session (
7489 id = session_id ,
75- fmu_directory = fmu_directory ,
7690 user_fmu_directory = user_fmu_directory ,
7791 created_at = now ,
7892 expires_at = now + expiration_duration ,
@@ -82,7 +96,9 @@ async def create_session(
8296
8397 return session_id
8498
85- async def get_session (self : Self , session_id : str ) -> Session | None :
99+ async def get_session (
100+ self : Self , session_id : str
101+ ) -> Session | ProjectSession | None :
86102 """Get the session data for a session id."""
87103 session = await self ._retrieve_session (session_id )
88104 if not session :
@@ -102,14 +118,60 @@ async def get_session(self: Self, session_id: str) -> Session | None:
102118
103119
104120async def create_fmu_session (
105- fmu_directory : ProjectFMUDirectory ,
106121 user_fmu_directory : UserFMUDirectory ,
107122 expire_seconds : int = settings .SESSION_EXPIRE_SECONDS ,
108123) -> str :
109124 """Creates a new session and stores it in the session mananger."""
110- return await session_manager .create_session (
111- fmu_directory , user_fmu_directory , expire_seconds
112- )
125+ return await session_manager .create_session (user_fmu_directory , expire_seconds )
126+
127+
128+ async def add_fmu_project_to_session (
129+ session_id : str ,
130+ project_fmu_directory : ProjectFMUDirectory ,
131+ ) -> ProjectSession :
132+ """Adds an opened project FMU directory instance to the session.
133+
134+ Returns:
135+ The updated ProjectSession
136+
137+ Raises:
138+ SessionNotFoundError: If no session was found
139+ """
140+ session = await session_manager .get_session (session_id )
141+ if not session :
142+ raise SessionNotFoundError (f"No session with id { session_id } found" )
143+
144+ if isinstance (session , ProjectSession ):
145+ project_session = session
146+ project_session .project_fmu_directory = project_fmu_directory
147+ else :
148+ project_session = ProjectSession (
149+ ** asdict (session ), project_fmu_directory = project_fmu_directory
150+ )
151+ await session_manager ._store_session (session_id , project_session )
152+ return project_session
153+
154+
155+ async def remove_fmu_project_from_session (session_id : str ) -> Session :
156+ """Removes (closes) an open project FMU directory from a session.
157+
158+ Returns:
159+ The update session
160+
161+ Raises:
162+ SessionNotFoundError: If no session was found
163+ """
164+ maybe_project_session = await session_manager .get_session (session_id )
165+
166+ if maybe_project_session is None :
167+ raise SessionNotFoundError (f"No session with id { session_id } found" )
168+ if isinstance (maybe_project_session , Session ):
169+ return maybe_project_session
170+
171+ project_session_dict = asdict (maybe_project_session )
172+ session = Session (** project_session_dict )
173+ await session_manager ._store_session (session_id , session )
174+ return session
113175
114176
115177async def destroy_fmu_session (session_id : str ) -> None :
0 commit comments