From 44e313fa6c3ed517ffcb6ce86e64c5be1a6b18e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de> Date: Mon, 6 Mar 2023 16:14:56 +0100 Subject: [PATCH] Filter workflow versions when listing workflows #18 --- app/api/endpoints/workflow.py | 25 +++++++++++++++---------- app/crud/crud_workflow.py | 25 ++++++++++++++++--------- app/crud/crud_workflow_version.py | 4 +++- app/tests/api/test_workflow.py | 8 ++++++-- app/tests/crud/test_workflow.py | 2 +- 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/app/api/endpoints/workflow.py b/app/api/endpoints/workflow.py index 30499c8..8857eb7 100644 --- a/app/api/endpoints/workflow.py +++ b/app/api/endpoints/workflow.py @@ -38,8 +38,10 @@ async def list_workflows( max_length=30, description="Filter workflows by a substring in their name.", ), - filter_unpublished: bool = Query( - False, description="Filter Workflows with unpublished versions. Permission 'Workflow:list_filter' required" + version_status: list[WorkflowVersion.Status] + | None = Query( + None, + description=f"Which versions of the workflow to include in the response. Permission 'workflow:list_filter required'. Default {WorkflowVersion.Status.PUBLISHED.name} and {WorkflowVersion.Status.DEPRECATED.name}.", # noqa: E501 ), db: AsyncSession = Depends(get_db), authorization: Callable[[str], Awaitable[Any]] = Depends(workflow_authorization), @@ -54,8 +56,8 @@ async def list_workflows( Async database session to perform query on. Dependency Injection. name_substring : string | None, default None Filter workflows by a substring in their name. Query Parameter. - filter_unpublished : bool, default False - Filter workflows that have an unpublished version + version_status : list[clowmdb.models.WorkflowVersionReduced.Status] | None, default None + Status of Workflow versions to filter for to fetch. Query Parameter. authorization : Callable[[str], Awaitable[Any]] Async function to ask the auth service for authorization. Dependency Injection. @@ -64,13 +66,16 @@ async def list_workflows( workflows : list[app.schemas.workflow.WorkflowOut] Workflows in the system """ - rbac_operation = "list_filter" if filter_unpublished else "list" + rbac_operation = "list_filter" if version_status is not None else "list" await authorization(rbac_operation) - workflows = await CRUDWorkflow.list(db, name_substring=name_substring) - return [ - WorkflowOut.from_db_workflow(workflow, versions=await CRUDWorkflowVersion.list(db, wid=workflow.workflow_id)) - for workflow in workflows - ] + workflows: list[Workflow] = await CRUDWorkflow.list( + db, + name_substring=name_substring, + version_status=[WorkflowVersion.Status.PUBLISHED, WorkflowVersion.Status.DEPRECATED] + if version_status is None + else version_status, + ) + return [WorkflowOut.from_db_workflow(workflow, versions=workflow.versions) for workflow in workflows] @router.post("", status_code=status.HTTP_201_CREATED, summary="Create a new workflow") diff --git a/app/crud/crud_workflow.py b/app/crud/crud_workflow.py index 968e7dc..3a919d9 100644 --- a/app/crud/crud_workflow.py +++ b/app/crud/crud_workflow.py @@ -1,8 +1,9 @@ from uuid import UUID from clowmdb.models import Workflow, WorkflowVersion -from sqlalchemy import delete, select +from sqlalchemy import delete, or_, select from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import joinedload from app.crud.crud_workflow_version import CRUDWorkflowVersion from app.schemas.workflow import WorkflowIn @@ -11,10 +12,12 @@ from app.schemas.workflow import WorkflowIn class CRUDWorkflow: @staticmethod async def list( - db: AsyncSession, name_substring: str | None = None, unpublished_version: bool = False + db: AsyncSession, + name_substring: str | None = None, + version_status: list[WorkflowVersion.Status] | None = None, ) -> list[Workflow]: """ - List all workflows. + List all workflows. Populates the version attribute of the workflows. Parameters ---------- @@ -22,20 +25,24 @@ class CRUDWorkflow: Async database session to perform query on. name_substring : str | None, default None Substring to filter for in the name of a workflow. - unpublished_version : bool, default False - Filter for workflows that have an unpublished version + version_status : list[clowmdb.models.WorkflowVersion.Status] | None, default None + Filter versions of a workflow based on the status. Removes workflows that have no version after this filter. Returns ------- workflows : list[app.models.user.User] List of workflows. """ - stmt = select(Workflow) + stmt = select(Workflow).options(joinedload(Workflow.versions)) if name_substring is not None: stmt = stmt.where(Workflow.name.contains(name_substring)) - if unpublished_version: - stmt = stmt.where(Workflow.versions.any(WorkflowVersion.status == WorkflowVersion.Status.CREATED)) - return (await db.execute(stmt)).scalars().all() + if version_status is not None: + stmt = stmt.options( + joinedload( + Workflow.versions.and_(or_(*[WorkflowVersion.status == status for status in version_status])) + ) + ) + return [w for w in (await db.execute(stmt)).scalars().unique().all() if len(w.versions) > 0] @staticmethod async def delete(db: AsyncSession, workflow_id: UUID | bytes) -> None: diff --git a/app/crud/crud_workflow_version.py b/app/crud/crud_workflow_version.py index 8b51189..9e1f5cd 100644 --- a/app/crud/crud_workflow_version.py +++ b/app/crud/crud_workflow_version.py @@ -18,6 +18,8 @@ class CRUDWorkflowVersion: Async database session to perform query on. git_commit_hash : str Git commit git_commit_hash of the version. + populate_workflow: boolean, default False + Flag if to populate the workflow attribute. Returns ------- @@ -78,7 +80,7 @@ class CRUDWorkflowVersion: Async database session to perform query on. wid : bytes | uuid.UUID Git commit git_commit_hash of the version. - version_status : list[clowmdb.models.WorkflowVersion.Status] | None, default + version_status : list[clowmdb.models.WorkflowVersion.Status] | None, default None Filter versions based on the status Returns diff --git a/app/tests/api/test_workflow.py b/app/tests/api/test_workflow.py index 4d7f776..b66c05d 100644 --- a/app/tests/api/test_workflow.py +++ b/app/tests/api/test_workflow.py @@ -257,7 +257,7 @@ class TestWorkflowRoutesList(_TestWorkflowRoutes): self, client: AsyncClient, random_user: UserWithAuthHeader, random_workflow: WorkflowOut ) -> None: """ - Test for creating a workflow where the git commit is already in the system. + Test for listing all workflows in the system. Parameters ---------- @@ -268,7 +268,11 @@ class TestWorkflowRoutesList(_TestWorkflowRoutes): random_workflow : app.schemas.workflow.WorkflowOut Random workflow for testing. pytest fixture. """ - response = await client.get(self.base_path, headers=random_user.auth_headers) + response = await client.get( + self.base_path, + headers=random_user.auth_headers, + params={"version_status": WorkflowVersion.Status.CREATED.name}, + ) assert response.status_code == status.HTTP_200_OK workflows = response.json() assert len(workflows) == 1 diff --git a/app/tests/crud/test_workflow.py b/app/tests/crud/test_workflow.py index b95ed14..e878f66 100644 --- a/app/tests/crud/test_workflow.py +++ b/app/tests/crud/test_workflow.py @@ -40,7 +40,7 @@ class TestWorkflowCRUDGet: random_workflow : app.schemas.workflow.WorkflowOut Random bucket for testing. pytest fixture. """ - workflows = await CRUDWorkflow.list(db, unpublished_version=True) + workflows = await CRUDWorkflow.list(db, version_status=[WorkflowVersion.Status.CREATED]) assert len(workflows) == 1 assert workflows[0].workflow_id == random_workflow.workflow_id -- GitLab