Skip to content

Commit 6d77327

Browse files
authored
Merge pull request #581 from populationgenomics/dev
Release
2 parents 4cb1744 + 8dcd99e commit 6d77327

File tree

18 files changed

+1048
-620
lines changed

18 files changed

+1048
-620
lines changed

.github/workflows/deploy.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ jobs:
2020
CLOUDSDK_CORE_DISABLE_PROMPTS: 1
2121
# used for generating API
2222
SM_DOCKER: australia-southeast1-docker.pkg.dev/sample-metadata/images/server:${{ github.sha }}
23-
SM_API_DOCKER: australia-southeast1-docker.pkg.dev/cpg-common/images/sm-api
2423
defaults:
2524
run:
2625
shell: bash -eo pipefail -l {0}

.pre-commit-config.yaml

+2-3
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,14 @@ repos:
4848
rev: v1.5.1
4949
hooks:
5050
- id: mypy
51-
args:
52-
[
51+
args: [
5352
--pretty,
5453
--show-error-codes,
5554
--no-strict-optional,
5655
--ignore-missing-imports,
5756
--install-types,
5857
--non-interactive,
59-
--show-error-context,
58+
# --show-error-context,
6059
--check-untyped-defs,
6160
--explicit-package-bases,
6261
--disable-error-code,

api/graphql/loaders.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,9 @@ async def load_projects_for_ids(project_ids: list[int], connection) -> list[Proj
333333
Get projects by IDs
334334
"""
335335
pttable = ProjectPermissionsTable(connection.connection)
336-
projects = await pttable.get_projects_by_ids(project_ids)
336+
projects = await pttable.get_and_check_access_to_projects_for_ids(
337+
user=connection.user, project_ids=project_ids, readonly=True
338+
)
337339
p_by_id = {p.id: p for p in projects}
338340
return [p_by_id.get(p) for p in project_ids]
339341

api/graphql/schema.py

+15-16
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ class GraphQLProject:
7272
name: str
7373
dataset: str
7474
meta: strawberry.scalars.JSON
75-
read_group_name: str | None = None
76-
write_group_name: str | None = None
7775

7876
@staticmethod
7977
def from_internal(internal: Project) -> 'GraphQLProject':
@@ -82,8 +80,6 @@ def from_internal(internal: Project) -> 'GraphQLProject':
8280
name=internal.name,
8381
dataset=internal.dataset,
8482
meta=internal.meta,
85-
read_group_name=internal.read_group_name,
86-
write_group_name=internal.write_group_name,
8783
)
8884

8985
@strawberry.field()
@@ -492,9 +488,11 @@ async def analyses(
492488
if project:
493489
ptable = ProjectPermissionsTable(connection.connection)
494490
project_ids = project.all_values()
495-
project_id_map = await ptable.get_project_id_map_for_names(
496-
author=connection.author, project_names=project_ids, readonly=True
491+
projects = await ptable.get_and_check_access_to_projects_for_names(
492+
user=connection.author, project_names=project_ids, readonly=True
497493
)
494+
project_id_map = {p.name: p.id for p in projects}
495+
498496
analyses = await loader.load(
499497
{
500498
'id': root.internal_id,
@@ -562,11 +560,10 @@ def enum(self, info: Info) -> GraphQLEnum:
562560
async def project(self, info: Info, name: str) -> GraphQLProject:
563561
connection = info.context['connection']
564562
ptable = ProjectPermissionsTable(connection.connection)
565-
project_id = await ptable.get_project_id_from_name_and_user(
563+
project = await ptable.get_and_check_access_to_project_for_name(
566564
user=connection.author, project_name=name, readonly=True
567565
)
568-
presponse = await ptable.get_projects_by_ids([project_id])
569-
return GraphQLProject.from_internal(presponse[0])
566+
return GraphQLProject.from_internal(project)
570567

571568
@strawberry.field
572569
async def sample(
@@ -590,11 +587,13 @@ async def sample(
590587
if external_id and not project:
591588
raise ValueError('Must provide project when using external_id filter')
592589

590+
project_name_map: dict[str, int] = {}
593591
if project:
594592
project_ids = project.all_values()
595-
project_id_map = await ptable.get_project_id_map_for_names(
596-
author=connection.author, project_names=project_ids, readonly=True
593+
projects = await ptable.get_and_check_access_to_projects_for_ids(
594+
user=connection.author, project_ids=project_ids, readonly=True
597595
)
596+
project_name_map = {p.name: p.id for p in projects}
598597

599598
filter_ = SampleFilter(
600599
id=id.to_internal_filter(sample_id_transform_to_raw) if id else None,
@@ -604,7 +603,7 @@ async def sample(
604603
participant_id=participant_id.to_internal_filter()
605604
if participant_id
606605
else None,
607-
project=project.to_internal_filter(lambda pname: project_id_map[pname])
606+
project=project.to_internal_filter(lambda pname: project_name_map[pname])
608607
if project
609608
else None,
610609
active=active.to_internal_filter() if active else GenericFilter(eq=True),
@@ -635,9 +634,10 @@ async def sequencing_groups(
635634
project_id_map = {}
636635
if project:
637636
project_ids = project.all_values()
638-
project_id_map = await ptable.get_project_id_map_for_names(
639-
author=connection.author, project_names=project_ids, readonly=True
637+
projects = await ptable.get_and_check_access_to_projects_for_ids(
638+
user=connection.author, project_ids=project_ids, readonly=True
640639
)
640+
project_id_map = {p.name: p.id for p in projects}
641641

642642
filter_ = SequencingGroupFilter(
643643
project=project.to_internal_filter(lambda val: project_id_map[val])
@@ -681,10 +681,9 @@ async def family(self, info: Info, family_id: int) -> GraphQLFamily:
681681
async def my_projects(self, info: Info) -> list[GraphQLProject]:
682682
connection = info.context['connection']
683683
ptable = ProjectPermissionsTable(connection.connection)
684-
project_map = await ptable.get_projects_accessible_by_user(
684+
projects = await ptable.get_projects_accessible_by_user(
685685
connection.author, readonly=True
686686
)
687-
projects = await ptable.get_projects_by_ids(list(project_map.keys()))
688687
return [GraphQLProject.from_internal(p) for p in projects]
689688

690689

api/routes/analysis.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,10 @@ async def query_analyses(
227227
raise ValueError('Must specify "projects"')
228228

229229
pt = ProjectPermissionsTable(connection=connection.connection)
230-
project_name_map = await pt.get_project_id_map_for_names(
231-
author=connection.author, project_names=query.projects, readonly=True
230+
projects = await pt.get_and_check_access_to_projects_for_names(
231+
user=connection.author, project_names=query.projects, readonly=True
232232
)
233+
project_name_map = {p.name: p.id for p in projects}
233234
atable = AnalysisLayer(connection)
234235
analyses = await atable.query(query.to_filter(project_name_map))
235236
return [a.to_external() for a in analyses]

api/routes/project.py

+36-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List, Optional
1+
from typing import List
22

33
from fastapi import APIRouter
44

@@ -13,48 +13,43 @@
1313
async def get_all_projects(connection=get_projectless_db_connection):
1414
"""Get list of projects"""
1515
ptable = ProjectPermissionsTable(connection.connection)
16-
return await ptable.get_project_rows(
17-
author=connection.author, check_permissions=False
18-
)
16+
return await ptable.get_all_projects(author=connection.author)
1917

2018

2119
@router.get('/', operation_id='getMyProjects', response_model=List[str])
2220
async def get_my_projects(connection=get_projectless_db_connection):
2321
"""Get projects I have access to"""
2422
ptable = ProjectPermissionsTable(connection.connection)
25-
pmap = await ptable.get_projects_accessible_by_user(
23+
projects = await ptable.get_projects_accessible_by_user(
2624
author=connection.author, readonly=True
2725
)
28-
return list(pmap.values())
26+
return [p.name for p in projects]
2927

3028

3129
@router.put('/', operation_id='createProject')
3230
async def create_project(
3331
name: str,
3432
dataset: str,
35-
read_group_name: Optional[str] = None,
36-
write_group_name: Optional[str] = None,
37-
create_test_project: bool = True,
33+
create_test_project: bool = False,
3834
connection: Connection = get_projectless_db_connection,
3935
) -> int:
4036
"""
4137
Create a new project
4238
"""
43-
if not read_group_name:
44-
read_group_name = f'{dataset}-sample-metadata-main-read'
45-
if not write_group_name:
46-
write_group_name = f'{dataset}-sample-metadata-main-write'
47-
4839
ptable = ProjectPermissionsTable(connection.connection)
4940
pid = await ptable.create_project(
5041
project_name=name,
5142
dataset_name=dataset,
52-
read_group_name=read_group_name,
53-
write_group_name=write_group_name,
54-
create_test_project=create_test_project,
5543
author=connection.author,
5644
)
5745

46+
if create_test_project:
47+
await ptable.create_project(
48+
project_name=name + '-test',
49+
dataset_name=dataset,
50+
author=connection.author,
51+
)
52+
5853
return pid
5954

6055

@@ -90,11 +85,32 @@ async def delete_project_data(
9085
Requires READ access + project-creator permissions
9186
"""
9287
ptable = ProjectPermissionsTable(connection.connection)
93-
pid = await ptable.get_project_id_from_name_and_user(
94-
connection.author, project, readonly=False
88+
p_obj = await ptable.get_and_check_access_to_project_for_name(
89+
user=connection.author, project_name=project, readonly=False
9590
)
9691
success = await ptable.delete_project_data(
97-
project_id=pid, delete_project=delete_project, author=connection.author
92+
project_id=p_obj.id, delete_project=delete_project, author=connection.author
9893
)
9994

10095
return {'success': success}
96+
97+
98+
@router.patch('/{project}/members', operation_id='updateProjectMembers')
99+
async def update_project_members(
100+
project: str,
101+
members: list[str],
102+
readonly: bool,
103+
connection: Connection = get_projectless_db_connection,
104+
):
105+
"""
106+
Update project members for specific read / write group.
107+
Not that this is protected by access to a specific access group
108+
"""
109+
ptable = ProjectPermissionsTable(connection.connection)
110+
await ptable.set_group_members(
111+
group_name=ptable.get_project_group_name(project, readonly=readonly),
112+
members=members,
113+
author=connection.author,
114+
)
115+
116+
return {'success': True}

api/routes/web.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77
from pydantic import BaseModel
88

99
from api.utils.db import (
10+
Connection,
1011
get_project_readonly_connection,
11-
get_projectless_db_connection,
1212
get_project_write_connection,
13-
Connection,
13+
get_projectless_db_connection,
1414
)
1515
from db.python.layers.search import SearchLayer
1616
from db.python.layers.seqr import SeqrLayer
17-
from db.python.layers.web import WebLayer, SearchItem
17+
from db.python.layers.web import SearchItem, WebLayer
1818
from db.python.tables.project import ProjectPermissionsTable
1919
from models.models.search import SearchResponse
20-
from models.models.web import ProjectSummary, PagingLinks
20+
from models.models.web import PagingLinks, ProjectSummary
2121

2222

2323
class SearchResponseModel(BaseModel):
@@ -81,7 +81,7 @@ async def search_by_keyword(keyword: str, connection=get_projectless_db_connecti
8181
projects = await pt.get_projects_accessible_by_user(
8282
connection.author, readonly=True
8383
)
84-
project_ids = list(projects.keys())
84+
project_ids = [p.id for p in projects]
8585
responses = await SearchLayer(connection).search(keyword, project_ids=project_ids)
8686

8787
for res in responses:
@@ -90,7 +90,9 @@ async def search_by_keyword(keyword: str, connection=get_projectless_db_connecti
9090
return SearchResponseModel(responses=responses)
9191

9292

93-
@router.post('/{project}/{sequencing_type}/sync-dataset', operation_id='syncSeqrProject')
93+
@router.post(
94+
'/{project}/{sequencing_type}/sync-dataset', operation_id='syncSeqrProject'
95+
)
9496
async def sync_seqr_project(
9597
sequencing_type: str,
9698
sync_families: bool = True,

0 commit comments

Comments
 (0)