1+ import logging
2+ from datetime import datetime
3+
14import sqlalchemy
5+ from aiohttp import web
26from aiopg .sa .engine import Engine
7+ from models_library .groups import GroupID
38from models_library .projects import ProjectID
49from models_library .users import UserID
10+ from pydantic import BaseModel , ConfigDict , TypeAdapter
11+ from simcore_postgres_database .models .project_to_groups import project_to_groups
512from simcore_postgres_database .models .projects import projects
13+ from simcore_postgres_database .utils_repos import transaction_context
14+ from sqlalchemy import func , literal_column
15+ from sqlalchemy .dialects .postgresql import insert as pg_insert
16+ from sqlalchemy .ext .asyncio import AsyncConnection
17+ from sqlalchemy .sql import select
18+
19+ from ..db .plugin import get_asyncpg_engine
20+ from .exceptions import ProjectGroupNotFoundError , ProjectNotFoundError
621
7- from . exceptions import ProjectNotFoundError
22+ _logger = logging . getLogger ( __name__ )
823
924
1025async def get_project_owner (engine : Engine , project_uuid : ProjectID ) -> UserID :
@@ -18,3 +33,199 @@ async def get_project_owner(engine: Engine, project_uuid: ProjectID) -> UserID:
1833 raise ProjectNotFoundError (project_uuid = project_uuid )
1934 assert isinstance (owner_id , int )
2035 return owner_id
36+
37+
38+ class ProjectGroupGetDB (BaseModel ):
39+ gid : GroupID
40+ read : bool
41+ write : bool
42+ delete : bool
43+ created : datetime
44+ modified : datetime
45+
46+ model_config = ConfigDict (from_attributes = True )
47+
48+
49+ async def create_project_group (
50+ app : web .Application ,
51+ connection : AsyncConnection | None = None ,
52+ * ,
53+ project_id : ProjectID ,
54+ group_id : GroupID ,
55+ read : bool ,
56+ write : bool ,
57+ delete : bool ,
58+ ) -> ProjectGroupGetDB :
59+ query = (
60+ project_to_groups .insert ()
61+ .values (
62+ project_uuid = f"{ project_id } " ,
63+ gid = group_id ,
64+ read = read ,
65+ write = write ,
66+ delete = delete ,
67+ created = func .now (),
68+ modified = func .now (),
69+ )
70+ .returning (literal_column ("*" ))
71+ )
72+
73+ async with transaction_context (get_asyncpg_engine (app ), connection ) as conn :
74+ result = await conn .stream (query )
75+ row = await result .first ()
76+ return ProjectGroupGetDB .model_validate (row )
77+
78+
79+ async def list_project_groups (
80+ app : web .Application ,
81+ connection : AsyncConnection | None = None ,
82+ * ,
83+ project_id : ProjectID ,
84+ ) -> list [ProjectGroupGetDB ]:
85+ stmt = (
86+ select (
87+ project_to_groups .c .gid ,
88+ project_to_groups .c .read ,
89+ project_to_groups .c .write ,
90+ project_to_groups .c .delete ,
91+ project_to_groups .c .created ,
92+ project_to_groups .c .modified ,
93+ )
94+ .select_from (project_to_groups )
95+ .where (project_to_groups .c .project_uuid == f"{ project_id } " )
96+ )
97+
98+ async with transaction_context (get_asyncpg_engine (app ), connection ) as conn :
99+ result = await conn .stream (stmt )
100+ rows = await result .all () or []
101+ return TypeAdapter (list [ProjectGroupGetDB ]).validate_python (rows )
102+
103+
104+ async def get_project_group (
105+ app : web .Application ,
106+ connection : AsyncConnection | None = None ,
107+ * ,
108+ project_id : ProjectID ,
109+ group_id : GroupID ,
110+ ) -> ProjectGroupGetDB :
111+ stmt = (
112+ select (
113+ project_to_groups .c .gid ,
114+ project_to_groups .c .read ,
115+ project_to_groups .c .write ,
116+ project_to_groups .c .delete ,
117+ project_to_groups .c .created ,
118+ project_to_groups .c .modified ,
119+ )
120+ .select_from (project_to_groups )
121+ .where (
122+ (project_to_groups .c .project_uuid == f"{ project_id } " )
123+ & (project_to_groups .c .gid == group_id )
124+ )
125+ )
126+
127+ async with transaction_context (get_asyncpg_engine (app ), connection ) as conn :
128+ result = await conn .stream (stmt )
129+ row = await result .first ()
130+ if row is None :
131+ raise ProjectGroupNotFoundError (
132+ reason = f"Project { project_id } group { group_id } not found"
133+ )
134+ return ProjectGroupGetDB .model_validate (row )
135+
136+
137+ async def replace_project_group (
138+ app : web .Application ,
139+ connection : AsyncConnection | None = None ,
140+ * ,
141+ project_id : ProjectID ,
142+ group_id : GroupID ,
143+ read : bool ,
144+ write : bool ,
145+ delete : bool ,
146+ ) -> ProjectGroupGetDB :
147+
148+ query = (
149+ project_to_groups .update ()
150+ .values (
151+ read = read ,
152+ write = write ,
153+ delete = delete ,
154+ )
155+ .where (
156+ (project_to_groups .c .project_uuid == f"{ project_id } " )
157+ & (project_to_groups .c .gid == group_id )
158+ )
159+ .returning (literal_column ("*" ))
160+ )
161+
162+ async with transaction_context (get_asyncpg_engine (app ), connection ) as conn :
163+ result = await conn .stream (query )
164+ row = await result .first ()
165+ if row is None :
166+ raise ProjectGroupNotFoundError (
167+ reason = f"Project { project_id } group { group_id } not found"
168+ )
169+ return ProjectGroupGetDB .model_validate (row )
170+
171+
172+ async def update_or_insert_project_group (
173+ app : web .Application ,
174+ connection : AsyncConnection | None = None ,
175+ * ,
176+ project_id : ProjectID ,
177+ group_id : GroupID ,
178+ read : bool ,
179+ write : bool ,
180+ delete : bool ,
181+ ) -> None :
182+ async with transaction_context (get_asyncpg_engine (app ), connection ) as conn :
183+ insert_stmt = pg_insert (project_to_groups ).values (
184+ project_uuid = f"{ project_id } " ,
185+ gid = group_id ,
186+ read = read ,
187+ write = write ,
188+ delete = delete ,
189+ created = func .now (),
190+ modified = func .now (),
191+ )
192+ on_update_stmt = insert_stmt .on_conflict_do_update (
193+ index_elements = [project_to_groups .c .project_uuid , project_to_groups .c .gid ],
194+ set_ = {
195+ "read" : insert_stmt .excluded .read ,
196+ "write" : insert_stmt .excluded .write ,
197+ "delete" : insert_stmt .excluded .delete ,
198+ "modified" : func .now (),
199+ },
200+ )
201+ await conn .stream (on_update_stmt )
202+
203+
204+ async def delete_project_group (
205+ app : web .Application ,
206+ connection : AsyncConnection | None = None ,
207+ * ,
208+ project_id : ProjectID ,
209+ group_id : GroupID ,
210+ ) -> None :
211+ async with transaction_context (get_asyncpg_engine (app ), connection ) as conn :
212+ await conn .stream (
213+ project_to_groups .delete ().where (
214+ (project_to_groups .c .project_uuid == f"{ project_id } " )
215+ & (project_to_groups .c .gid == group_id )
216+ )
217+ )
218+
219+
220+ async def delete_all_project_groups (
221+ app : web .Application ,
222+ connection : AsyncConnection | None = None ,
223+ * ,
224+ project_id : ProjectID ,
225+ ) -> None :
226+ async with transaction_context (get_asyncpg_engine (app ), connection ) as conn :
227+ await conn .stream (
228+ project_to_groups .delete ().where (
229+ project_to_groups .c .project_uuid == f"{ project_id } "
230+ )
231+ )
0 commit comments