1
+ import logging
2
+ from datetime import datetime
3
+
1
4
import sqlalchemy
5
+ from aiohttp import web
2
6
from aiopg .sa .engine import Engine
7
+ from models_library .groups import GroupID
3
8
from models_library .projects import ProjectID
4
9
from 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
5
12
from 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
6
21
7
- from . exceptions import ProjectNotFoundError
22
+ _logger = logging . getLogger ( __name__ )
8
23
9
24
10
25
async 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:
18
33
raise ProjectNotFoundError (project_uuid = project_uuid )
19
34
assert isinstance (owner_id , int )
20
35
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