diff --git a/app/api/endpoints/workflow.py b/app/api/endpoints/workflow.py index 30499c82d899d2353050d42a1f7f0095a49f0e60..8857eb79e1cbba6d7ce225a53fca59b9c8e4f62e 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 968e7dc6eb60ee6a1f982270a914d69555f418de..3a919d9199c6662ab735a6b8398bbc07bf2551b7 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 8b511899546e5dd33eb4a2487b32b8d6087f8a08..9e1f5cdc8ec23c834c19b4612365dd0d92541642 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 4d7f776c2f76fcb2b6947e36fc2a210e503f6dec..b66c05d3e37f5aa1cb869b458f97e9baa7f69326 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 b95ed143b76388ccf5f08d0e8c44a36765dde7a6..e878f6621f6dc942db176b91e5ef28a878141768 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