Skip to content

Commit 723d286

Browse files
committed
validation now done through pydantic
validation done when loading from DB filtering of projects done directly after load
1 parent 151a0fb commit 723d286

File tree

2 files changed

+51
-38
lines changed

2 files changed

+51
-38
lines changed

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

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
- Shall be used as entry point for all the queries to the database regarding projects
55
66
"""
7-
7+
import asyncio
88
import logging
99
import textwrap
1010
import uuid as uuidlib
@@ -22,6 +22,8 @@
2222
from aiopg.sa.connection import SAConnection
2323
from aiopg.sa.result import ResultProxy, RowProxy
2424
from change_case import ChangeCase
25+
from jsonschema.exceptions import ValidationError
26+
from models_library.projects import ProjectAtDB
2527
from servicelib.application_keys import APP_DB_ENGINE_KEY
2628
from simcore_postgres_database.webserver_models import ProjectType, projects
2729
from sqlalchemy import literal_column
@@ -37,6 +39,7 @@
3739
ProjectsException,
3840
)
3941
from .projects_fakes import Fake
42+
from .projects_utils import project_uses_available_services
4043

4144
log = logging.getLogger(__name__)
4245

@@ -307,7 +310,11 @@ async def add_project(
307310
prj["tags"] = []
308311
return prj
309312

310-
async def load_user_projects(self, user_id: int) -> List[Dict]:
313+
async def load_user_projects(
314+
self,
315+
user_id: int,
316+
filter_by_services: Optional[List[Dict]] = None,
317+
) -> List[Dict]:
311318
log.info("Loading projects for user %s", user_id)
312319
async with self.engine.acquire() as conn:
313320
user_groups: List[RowProxy] = await self.__load_user_groups(conn, user_id)
@@ -330,13 +337,17 @@ async def load_user_projects(self, user_id: int) -> List[Dict]:
330337
"""
331338
)
332339
projects_list = await self.__load_projects(
333-
conn, query, user_id, user_groups
340+
conn, query, user_id, user_groups, filter_by_services=filter_by_services
334341
)
335342

336343
return projects_list
337344

338345
async def load_template_projects(
339-
self, user_id: int, *, only_published=False
346+
self,
347+
user_id: int,
348+
*,
349+
only_published=False,
350+
filter_by_services: Optional[List[Dict]] = None,
340351
) -> List[Dict]:
341352
log.info("Loading public template projects")
342353

@@ -358,7 +369,9 @@ async def load_template_projects(
358369
"""
359370
)
360371

361-
db_projects = await self.__load_projects(conn, query, user_id, user_groups)
372+
db_projects = await self.__load_projects(
373+
conn, query, user_id, user_groups, filter_by_services
374+
)
362375

363376
projects_list.extend(db_projects)
364377

@@ -378,7 +391,12 @@ async def __load_user_groups(
378391
return user_groups
379392

380393
async def __load_projects(
381-
self, conn: SAConnection, query: str, user_id: int, user_groups: List[RowProxy]
394+
self,
395+
conn: SAConnection,
396+
query: str,
397+
user_id: int,
398+
user_groups: List[RowProxy],
399+
filter_by_services: Optional[List[Dict]] = None,
382400
) -> List[Dict]:
383401
api_projects: List[Dict] = [] # API model-compatible projects
384402
db_projects: List[Dict] = [] # DB model-compatible projects
@@ -387,7 +405,21 @@ async def __load_projects(
387405
_check_project_permissions(row, user_id, user_groups, "read")
388406
except ProjectInvalidRightsError:
389407
continue
408+
try:
409+
import concurrent.futures
410+
411+
with concurrent.futures.ThreadPoolExecutor() as pool:
412+
await asyncio.get_event_loop().run_in_executor(
413+
pool, ProjectAtDB.from_orm, row
414+
)
415+
# ProjectAtDB.from_orm(row)
416+
except ValidationError as exc:
417+
log.warning("project failed validation: %s", exc)
418+
continue
390419
prj = dict(row.items())
420+
if filter_by_services:
421+
if not await project_uses_available_services(prj, filter_by_services):
422+
continue
391423
db_projects.append(prj)
392424

393425
# NOTE: DO NOT nest _get_tags_by_project in async loop above !!!

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

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
""" Handlers for CRUD operations on /projects/
22
33
"""
4-
import asyncio
54
import json
65
import logging
76
from typing import Any, Dict, List, Optional, Set
@@ -143,14 +142,24 @@ async def set_all_project_states(projects: List[Dict[str, Any]], is_template: bo
143142
reraise=True,
144143
)
145144

145+
user_available_services: List[
146+
Dict
147+
] = await catalog.get_services_for_user_in_product(
148+
request.app, user_id, product_name, only_key_versions=True
149+
)
150+
146151
projects_list = []
147152
if ptype in ("template", "all"):
148-
template_projects = await db.load_template_projects(user_id=user_id)
153+
template_projects = await db.load_template_projects(
154+
user_id=user_id, filter_by_services=user_available_services
155+
)
149156
await set_all_project_states(template_projects, is_template=True)
150157
projects_list += template_projects
151158

152159
if ptype in ("user", "all"): # standard only (notice that templates will only)
153-
user_projects = await db.load_user_projects(user_id=user_id)
160+
user_projects = await db.load_user_projects(
161+
user_id=user_id, filter_by_services=user_available_services
162+
)
154163
await set_all_project_states(user_projects, is_template=False)
155164
projects_list += user_projects
156165

@@ -159,35 +168,7 @@ async def set_all_project_states(projects: List[Dict[str, Any]], is_template: bo
159168

160169
stop = min(start + count, len(projects_list))
161170
projects_list = projects_list[start:stop]
162-
user_available_services: List[
163-
Dict
164-
] = await catalog.get_services_for_user_in_product(
165-
request.app, user_id, product_name, only_key_versions=True
166-
)
167-
168-
# validate response
169-
async def validate_project(prj: Dict[str, Any]) -> Dict[str, Any]:
170-
try:
171-
172-
await asyncio.get_event_loop().run_in_executor(
173-
None, projects_api.validate_project, request.app, prj
174-
)
175-
if await project_uses_available_services(prj, user_available_services):
176-
return prj
177-
except ValidationError:
178-
log.warning(
179-
"Invalid project with id='%s' in database."
180-
"Skipping project from listed response."
181-
"RECOMMENDED db data diagnose and cleanup",
182-
prj.get("uuid", "undefined"),
183-
)
184-
185-
validation_tasks = [validate_project(project) for project in projects_list]
186-
# FIXME: if some invalid, then it should not reraise but instead
187-
results = await logged_gather(*validation_tasks, reraise=True)
188-
validated_projects = [r for r in results if r]
189-
190-
return {"data": validated_projects}
171+
return {"data": projects_list}
191172

192173

193174
@login_required

0 commit comments

Comments
 (0)