Skip to content

Commit 097a18d

Browse files
batch get project access rights sql query
1 parent dde02ee commit 097a18d

File tree

3 files changed

+78
-75
lines changed

3 files changed

+78
-75
lines changed
Lines changed: 73 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,23 @@
1-
from numpy import diff
21
import sqlalchemy
3-
from aiopg.sa.engine import Engine
4-
from models_library.projects import ProjectID
5-
from models_library.users import UserID
6-
from simcore_postgres_database.models.projects import projects
7-
import logging
8-
from collections.abc import Callable
9-
from datetime import datetime
10-
from typing import cast
11-
122
import sqlalchemy as sa
133
from aiohttp import web
14-
from common_library.exclude import UnSet, is_set
15-
from models_library.basic_types import IDStr
4+
from aiopg.sa.engine import Engine
165
from models_library.groups import GroupID
176
from models_library.projects import ProjectID
18-
from models_library.rest_ordering import OrderBy, OrderDirection
19-
from models_library.rest_pagination import MAXIMUM_NUMBER_OF_ITEMS_PER_PAGE
7+
from models_library.users import UserID
208
from models_library.workspaces import WorkspaceID
21-
from pydantic import NonNegativeInt, PositiveInt
22-
from simcore_postgres_database.models.projects import projects
239
from simcore_postgres_database.models.project_to_groups import project_to_groups
24-
from simcore_postgres_database.models.users import users
10+
from simcore_postgres_database.models.projects import projects
11+
from simcore_postgres_database.models.workspace_access_rights import (
12+
workspace_access_rights,
13+
)
2514
from simcore_postgres_database.utils_repos import (
26-
get_columns_from_db_model,
2715
pass_or_acquire_connection,
28-
transaction_context,
2916
)
30-
from sqlalchemy import sql
3117
from sqlalchemy.ext.asyncio import AsyncConnection
3218

3319
from ..db.plugin import get_asyncpg_engine
3420
from .exceptions import ProjectNotFoundError
35-
from .models import ProjectDBGet
36-
from .exceptions import ProjectNotFoundError
3721

3822

3923
async def get_project_owner(engine: Engine, project_uuid: ProjectID) -> UserID:
@@ -53,56 +37,75 @@ async def batch_get_project_access_rights(
5337
app: web.Application,
5438
connection: AsyncConnection | None = None,
5539
*,
56-
projects_uuids: list[ProjectID],
40+
projects_uuids_with_workspace_id: list[
41+
tuple[ProjectID, WorkspaceID | None]
42+
], # list of tuples (project_uuid, workspace_id)
5743
) -> dict[ProjectID, dict[GroupID, dict[str, bool]]]:
44+
# Split into private and shared workspace project IDs
45+
private_project_ids = [
46+
pid for pid, wid in projects_uuids_with_workspace_id if wid is None
47+
]
48+
shared_project_ids = [
49+
pid for pid, wid in projects_uuids_with_workspace_id if wid is not None
50+
]
5851

59-
# NOTE: MD: TODO: differentiate between private/shared workspaces
60-
# based on that use either project_to_groups or workspace_access_rights
52+
results = {}
6153

62-
private_workspace_access_rights_query = (
63-
sa.select(
64-
project_to_groups.c.project_uuid,
65-
sa.func.jsonb_object_agg(
66-
project_to_groups.c.gid,
67-
sa.func.jsonb_build_object(
68-
"read",
69-
project_to_groups.c.read,
70-
"write",
71-
project_to_groups.c.write,
72-
"delete",
73-
project_to_groups.c.delete,
74-
),
75-
).label("access_rights"),
76-
)
77-
.where(
78-
(projects.c.uuid.in_([f"{uuid}" for uuid in projects_uuids])) # <-- this needs to be prefiltered based on workspace
79-
& (project_to_groups.c.read)
80-
)
81-
.group_by(project_to_groups.c.project_uuid)
82-
)
54+
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
55+
# Query private workspace projects
56+
if private_project_ids:
57+
private_query = (
58+
sa.select(
59+
project_to_groups.c.project_uuid,
60+
sa.func.jsonb_object_agg(
61+
project_to_groups.c.gid,
62+
sa.func.jsonb_build_object(
63+
"read",
64+
project_to_groups.c.read,
65+
"write",
66+
project_to_groups.c.write,
67+
"delete",
68+
project_to_groups.c.delete,
69+
),
70+
).label("access_rights"),
71+
)
72+
.where(
73+
project_to_groups.c.project_uuid.in_(
74+
[f"{uuid}" for uuid in private_project_ids]
75+
)
76+
)
77+
.group_by(project_to_groups.c.project_uuid)
78+
)
79+
private_result = await conn.stream(private_query)
80+
async for row in private_result:
81+
results[row.project_uuid] = row.access_rights
8382

84-
shared_workspace_access_rights_query = (
85-
sa.select(
86-
workspace_access_rights.c.project_uuid,
87-
sa.func.jsonb_object_agg(
88-
project_to_groups.c.gid,
89-
sa.func.jsonb_build_object(
90-
"read",
91-
workspace_access_rights.c.read,
92-
"write",
93-
workspace_access_rights.c.write,
94-
"delete",
95-
workspace_access_rights.c.delete,
96-
),
97-
).label("access_rights"),
98-
)
99-
.where(
100-
(projects.c.uuid.in_([f"{uuid}" for uuid in projects_uuids])) # <-- this needs to be prefiltered based on workspace
101-
& (workspace_access_rights.c.read)
102-
)
103-
.group_by(workspace_access_rights.c.project_uuid)
104-
)
83+
# Query shared workspace projects
84+
if shared_project_ids:
85+
shared_query = (
86+
sa.select(
87+
workspace_access_rights.c.project_uuid,
88+
sa.func.jsonb_object_agg(
89+
workspace_access_rights.c.gid,
90+
sa.func.jsonb_build_object(
91+
"read",
92+
workspace_access_rights.c.read,
93+
"write",
94+
workspace_access_rights.c.write,
95+
"delete",
96+
workspace_access_rights.c.delete,
97+
),
98+
).label("access_rights"),
99+
)
100+
.where(
101+
workspace_access_rights.c.project_uuid.in_(
102+
[f"{uuid}" for uuid in shared_project_ids]
103+
)
104+
)
105+
.group_by(workspace_access_rights.c.project_uuid)
106+
)
107+
shared_result = await conn.stream(shared_query)
108+
async for row in shared_result:
109+
results[row.project_uuid] = row.access_rights
105110

106-
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
107-
result = await conn.stream(access_rights_query)
108-
return {row.project_uuid: row.access_rights async for row in result}
111+
return results

services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
ProjectTemplateType as ProjectTemplateTypeDB,
2222
)
2323
from simcore_postgres_database.webserver_models import ProjectType as ProjectTypeDB
24-
from ._access_rights_repository import batch_get_project_access_rights
2524

2625
from ..catalog import catalog_service
2726
from ..folders import _folders_repository
2827
from ..workspaces.api import check_user_workspace_access
2928
from . import _projects_service
29+
from ._access_rights_repository import batch_get_project_access_rights
3030
from ._projects_repository import batch_get_trashed_by_primary_gid
3131
from ._projects_repository_legacy import ProjectDBAPI
3232
from .models import ProjectDict, ProjectTypeAPI
@@ -69,7 +69,10 @@ async def _aggregate_data_to_projects_from_other_sources(
6969

7070
# Add here get batch Project access rights
7171
project_to_access_rights = await batch_get_project_access_rights(
72-
app=app, projects_uuids=[ProjectID(p["uuid"]) for p in db_projects]
72+
app=app,
73+
projects_uuids_with_workspace_id=[
74+
(ProjectID(p["uuid"]), p["workspaceId"]) for p in db_projects
75+
],
7376
)
7477

7578
# udpating `project.state`

services/web/server/src/simcore_service_webserver/projects/_projects_repository_legacy_utils.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,6 @@ async def _execute_without_permission_check(
233233
continue
234234
db_projects.append(prj)
235235

236-
# Here batch get project access rights
237-
238-
239236
# NOTE: DO NOT nest _get_tags_by_project in async loop above !!!
240237
# FIXME: temporary avoids inner async loops issue https://github.com/aio-libs/aiopg/issues/535
241238
for db_prj in db_projects:

0 commit comments

Comments
 (0)