Skip to content

Commit 143fdf7

Browse files
committed
Merge branch 'fix/info-button-in-table' of github.com:odeimaiz/osparc-simcore into fix/info-button-in-table
2 parents 974abac + 760a627 commit 143fdf7

File tree

4 files changed

+166
-93
lines changed

4 files changed

+166
-93
lines changed

packages/postgres-database/src/simcore_postgres_database/migration/versions/afb1ba08f3c2_add_functions_product_access.py

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def upgrade():
2020
# ### commands auto generated by Alembic - please adjust! ###
2121
op.add_column(
2222
"funcapi_function_job_collections_access_rights",
23-
sa.Column("product_name", sa.String(), nullable=False),
23+
sa.Column("product_name", sa.String(), nullable=True),
2424
)
2525
op.create_foreign_key(
2626
"fk_func_access_to_products_product_name",
@@ -33,7 +33,7 @@ def upgrade():
3333
)
3434
op.add_column(
3535
"funcapi_function_jobs_access_rights",
36-
sa.Column("product_name", sa.String(), nullable=False),
36+
sa.Column("product_name", sa.String(), nullable=True),
3737
)
3838
op.create_foreign_key(
3939
"fk_func_access_to_products_product_name",
@@ -46,7 +46,7 @@ def upgrade():
4646
)
4747
op.add_column(
4848
"funcapi_functions_access_rights",
49-
sa.Column("product_name", sa.String(), nullable=False),
49+
sa.Column("product_name", sa.String(), nullable=True),
5050
)
5151
op.create_foreign_key(
5252
"fk_func_access_to_products_product_name",
@@ -57,6 +57,49 @@ def upgrade():
5757
onupdate="CASCADE",
5858
ondelete="CASCADE",
5959
)
60+
61+
# Backfill existing rows with "osparc"
62+
op.execute(
63+
"""
64+
UPDATE funcapi_function_job_collections_access_rights
65+
SET product_name = 'osparc'
66+
WHERE product_name IS NULL
67+
"""
68+
)
69+
op.execute(
70+
"""
71+
UPDATE funcapi_function_jobs_access_rights
72+
SET product_name = 'osparc'
73+
WHERE product_name IS NULL
74+
"""
75+
)
76+
op.execute(
77+
"""
78+
UPDATE funcapi_functions_access_rights
79+
SET product_name = 'osparc'
80+
WHERE product_name IS NULL
81+
"""
82+
)
83+
84+
# Alter columns to set nullable=False
85+
op.alter_column(
86+
"funcapi_function_job_collections_access_rights",
87+
"product_name",
88+
existing_type=sa.String(),
89+
nullable=False,
90+
)
91+
op.alter_column(
92+
"funcapi_function_jobs_access_rights",
93+
"product_name",
94+
existing_type=sa.String(),
95+
nullable=False,
96+
)
97+
op.alter_column(
98+
"funcapi_functions_access_rights",
99+
"product_name",
100+
existing_type=sa.String(),
101+
nullable=False,
102+
)
60103
# ### end Alembic commands ###
61104

62105

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
1-
import sqlalchemy
1+
import sqlalchemy as sa
2+
from aiohttp import web
23
from aiopg.sa.engine import Engine
3-
from models_library.projects import ProjectID
4+
from models_library.groups import GroupID
5+
from models_library.projects import ProjectID, ProjectIDStr
46
from models_library.users import UserID
7+
from models_library.workspaces import WorkspaceID
8+
from simcore_postgres_database.models.project_to_groups import project_to_groups
59
from simcore_postgres_database.models.projects import projects
10+
from simcore_postgres_database.models.workspaces_access_rights import (
11+
workspaces_access_rights,
12+
)
13+
from simcore_postgres_database.utils_repos import (
14+
pass_or_acquire_connection,
15+
)
16+
from sqlalchemy.ext.asyncio import AsyncConnection
617

18+
from ..db.plugin import get_asyncpg_engine
719
from .exceptions import ProjectNotFoundError
820

921

1022
async def get_project_owner(engine: Engine, project_uuid: ProjectID) -> UserID:
1123
async with engine.acquire() as connection:
12-
stmt = sqlalchemy.select(projects.c.prj_owner).where(
24+
stmt = sa.select(projects.c.prj_owner).where(
1325
projects.c.uuid == f"{project_uuid}"
1426
)
1527

@@ -18,3 +30,94 @@ async def get_project_owner(engine: Engine, project_uuid: ProjectID) -> UserID:
1830
raise ProjectNotFoundError(project_uuid=project_uuid)
1931
assert isinstance(owner_id, int)
2032
return owner_id
33+
34+
35+
def _split_private_and_shared_projects(
36+
projects_uuids_with_workspace_id: list[tuple[ProjectID, WorkspaceID | None]],
37+
) -> tuple[list[ProjectID], dict[WorkspaceID, list[ProjectID]]]:
38+
"""Splits project tuples into private project IDs and a mapping of workspace_id to project IDs."""
39+
private_project_ids = []
40+
workspace_to_project_ids: dict[WorkspaceID, list[ProjectID]] = {}
41+
for pid, wid in projects_uuids_with_workspace_id:
42+
if wid is None:
43+
private_project_ids.append(pid)
44+
else:
45+
workspace_to_project_ids.setdefault(wid, []).append(pid)
46+
return private_project_ids, workspace_to_project_ids
47+
48+
49+
async def batch_get_project_access_rights(
50+
app: web.Application,
51+
connection: AsyncConnection | None = None,
52+
*,
53+
projects_uuids_with_workspace_id: list[tuple[ProjectID, WorkspaceID | None]],
54+
) -> dict[ProjectIDStr, dict[GroupID, dict[str, bool]]]:
55+
private_project_ids, workspace_to_project_ids = _split_private_and_shared_projects(
56+
projects_uuids_with_workspace_id
57+
)
58+
shared_workspace_ids = set(workspace_to_project_ids.keys())
59+
results = {}
60+
61+
async with pass_or_acquire_connection(get_asyncpg_engine(app), connection) as conn:
62+
# Query private workspace projects
63+
if private_project_ids:
64+
private_query = (
65+
sa.select(
66+
project_to_groups.c.project_uuid,
67+
sa.func.jsonb_object_agg(
68+
project_to_groups.c.gid,
69+
sa.func.jsonb_build_object(
70+
"read",
71+
project_to_groups.c.read,
72+
"write",
73+
project_to_groups.c.write,
74+
"delete",
75+
project_to_groups.c.delete,
76+
),
77+
).label("access_rights"),
78+
)
79+
.where(
80+
project_to_groups.c.project_uuid.in_(
81+
[f"{uuid}" for uuid in private_project_ids]
82+
)
83+
)
84+
.group_by(project_to_groups.c.project_uuid)
85+
)
86+
private_result = await conn.stream(private_query)
87+
async for row in private_result:
88+
results[row.project_uuid] = row.access_rights
89+
90+
# Query shared workspace projects by workspace_id
91+
if shared_workspace_ids:
92+
shared_query = (
93+
sa.select(
94+
workspaces_access_rights.c.workspace_id,
95+
sa.func.jsonb_object_agg(
96+
workspaces_access_rights.c.gid,
97+
sa.func.jsonb_build_object(
98+
"read",
99+
workspaces_access_rights.c.read,
100+
"write",
101+
workspaces_access_rights.c.write,
102+
"delete",
103+
workspaces_access_rights.c.delete,
104+
),
105+
).label("access_rights"),
106+
)
107+
.where(
108+
workspaces_access_rights.c.workspace_id.in_(shared_workspace_ids)
109+
)
110+
.group_by(workspaces_access_rights.c.workspace_id)
111+
)
112+
shared_result = await conn.stream(shared_query)
113+
workspace_access_rights_map = {}
114+
async for row in shared_result:
115+
workspace_access_rights_map[row.workspace_id] = row.access_rights
116+
# Assign access rights to each project in the workspace
117+
for wid, project_ids in workspace_to_project_ids.items():
118+
access_rights = workspace_access_rights_map.get(wid)
119+
if access_rights is not None:
120+
for pid in project_ids:
121+
results[f"{pid}"] = access_rights
122+
123+
return results

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from ..folders import _folders_repository
2727
from ..workspaces.api import check_user_workspace_access
2828
from . import _projects_service
29+
from ._access_rights_repository import batch_get_project_access_rights
2930
from ._projects_repository import batch_get_trashed_by_primary_gid
3031
from ._projects_repository_legacy import ProjectDBAPI
3132
from .models import ProjectDict, ProjectTypeAPI
@@ -66,6 +67,14 @@ async def _aggregate_data_to_projects_from_other_sources(
6667

6768
_batch_update("trashed_by_primary_gid", trashed_by_primary_gid_values, db_projects)
6869

70+
# Add here get batch Project access rights
71+
project_to_access_rights = await batch_get_project_access_rights(
72+
app=app,
73+
projects_uuids_with_workspace_id=[
74+
(ProjectID(p["uuid"]), p["workspaceId"]) for p in db_projects
75+
],
76+
)
77+
6978
# udpating `project.state`
7079
update_state_per_project = [
7180
_projects_service.add_project_states_for_user(
@@ -81,6 +90,9 @@ async def _aggregate_data_to_projects_from_other_sources(
8190
*update_state_per_project,
8291
)
8392

93+
for project in updated_projects:
94+
project["accessRights"] = project_to_access_rights[project["uuid"]]
95+
8496
return updated_projects
8597

8698

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

Lines changed: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -374,44 +374,8 @@ def _create_private_workspace_query(
374374
WorkspaceScope.ALL,
375375
)
376376

377-
access_rights_subquery = (
378-
sa.select(
379-
project_to_groups.c.project_uuid,
380-
sa.func.jsonb_object_agg(
381-
project_to_groups.c.gid,
382-
sa.func.jsonb_build_object(
383-
"read",
384-
project_to_groups.c.read,
385-
"write",
386-
project_to_groups.c.write,
387-
"delete",
388-
project_to_groups.c.delete,
389-
),
390-
).label("access_rights"),
391-
)
392-
.where(
393-
project_to_groups.c.project_uuid == projects.c.uuid
394-
) # Correlate with main query
395-
.where(project_to_groups.c.read)
396-
.group_by(project_to_groups.c.project_uuid)
397-
.lateral() # Critical for per-row execution
398-
)
399-
400377
my_access_rights_subquery = (
401-
sa.select(
402-
project_to_groups.c.project_uuid,
403-
sa.func.jsonb_object_agg(
404-
project_to_groups.c.gid,
405-
sa.func.jsonb_build_object(
406-
"read",
407-
project_to_groups.c.read,
408-
"write",
409-
project_to_groups.c.write,
410-
"delete",
411-
project_to_groups.c.delete,
412-
),
413-
).label("access_rights"),
414-
)
378+
sa.select(project_to_groups.c.project_uuid)
415379
.where(
416380
(
417381
project_to_groups.c.read
@@ -427,7 +391,6 @@ def _create_private_workspace_query(
427391
sa.select(
428392
*PROJECT_DB_COLS,
429393
projects.c.workbench,
430-
access_rights_subquery.c.access_rights,
431394
projects_to_products.c.product_name,
432395
projects_to_folders.c.folder_id,
433396
)
@@ -442,10 +405,6 @@ def _create_private_workspace_query(
442405
),
443406
isouter=True,
444407
)
445-
.join(
446-
access_rights_subquery,
447-
access_rights_subquery.c.project_uuid == projects.c.uuid,
448-
)
449408
)
450409
.where(
451410
(projects.c.workspace_id.is_(None)) # <-- Private workspace
@@ -479,46 +438,8 @@ def _create_shared_workspace_query(
479438
WorkspaceScope.ALL,
480439
)
481440

482-
workspace_access_rights_subquery = (
483-
(
484-
sa.select(
485-
workspaces_access_rights.c.workspace_id,
486-
sa.func.jsonb_object_agg(
487-
workspaces_access_rights.c.gid,
488-
sa.func.jsonb_build_object(
489-
"read",
490-
workspaces_access_rights.c.read,
491-
"write",
492-
workspaces_access_rights.c.write,
493-
"delete",
494-
workspaces_access_rights.c.delete,
495-
),
496-
).label("access_rights"),
497-
)
498-
.where(
499-
workspaces_access_rights.c.read,
500-
)
501-
.group_by(workspaces_access_rights.c.workspace_id)
502-
)
503-
.subquery("workspace_access_rights_subquery")
504-
.lateral()
505-
)
506-
507441
my_workspace_access_rights_subquery = (
508-
sa.select(
509-
workspaces_access_rights.c.workspace_id,
510-
sa.func.jsonb_object_agg(
511-
workspaces_access_rights.c.gid,
512-
sa.func.jsonb_build_object(
513-
"read",
514-
workspaces_access_rights.c.read,
515-
"write",
516-
workspaces_access_rights.c.write,
517-
"delete",
518-
workspaces_access_rights.c.delete,
519-
),
520-
).label("access_rights"),
521-
)
442+
sa.select(workspaces_access_rights.c.workspace_id)
522443
.where(
523444
workspaces_access_rights.c.read,
524445
workspaces_access_rights.c.gid.in_(user_groups),
@@ -530,7 +451,6 @@ def _create_shared_workspace_query(
530451
sa.select(
531452
*PROJECT_DB_COLS,
532453
projects.c.workbench,
533-
workspace_access_rights_subquery.c.access_rights,
534454
projects_to_products.c.product_name,
535455
projects_to_folders.c.folder_id,
536456
)
@@ -549,11 +469,6 @@ def _create_shared_workspace_query(
549469
),
550470
isouter=True,
551471
)
552-
.join(
553-
workspace_access_rights_subquery,
554-
projects.c.workspace_id
555-
== workspace_access_rights_subquery.c.workspace_id,
556-
)
557472
)
558473
.where(projects_to_products.c.product_name == product_name)
559474
)

0 commit comments

Comments
 (0)