From 3ca8617564b8c75c0a6ed62d92782e8d3fdcebc1 Mon Sep 17 00:00:00 2001 From: Tom Ward Date: Fri, 22 Mar 2024 15:50:10 +0000 Subject: [PATCH] create file approve & reject urls --- airlock/urls.py | 10 +++ airlock/views/__init__.py | 4 + airlock/views/request.py | 41 +++++++++ tests/integration/views/test_request.py | 112 ++++++++++++++++++++++++ 4 files changed, 167 insertions(+) diff --git a/airlock/urls.py b/airlock/urls.py index abd23841a..dfd629f58 100644 --- a/airlock/urls.py +++ b/airlock/urls.py @@ -77,6 +77,16 @@ airlock.views.request_contents, name="request_contents", ), + path( + "requests/approve//", + airlock.views.file_approve, + name="file_approve", + ), + path( + "requests/reject//", + airlock.views.file_reject, + name="file_reject", + ), path( "requests/release/", airlock.views.request_release_files, diff --git a/airlock/views/__init__.py b/airlock/views/__init__.py index a06d85b36..902afd60d 100644 --- a/airlock/views/__init__.py +++ b/airlock/views/__init__.py @@ -1,5 +1,7 @@ from .auth import login, logout from .request import ( + file_approve, + file_reject, request_contents, request_index, request_reject, @@ -19,6 +21,8 @@ "login", "logout", "index", + "file_approve", + "file_reject", "request_contents", "request_index", "request_reject", diff --git a/airlock/views/request.py b/airlock/views/request.py index 60a1eef8b..d3044a372 100644 --- a/airlock/views/request.py +++ b/airlock/views/request.py @@ -156,6 +156,47 @@ def request_reject(request, request_id): @instrument(func_attributes={"release_request": "request_id"}) @require_http_methods(["POST"]) +def file_approve(request, request_id, path: str): + release_request = get_release_request_or_raise(request.user, request_id) + + try: + relpath = release_request.get_request_file(path).relpath + except bll.FileNotFound: + raise Http404() + + try: + bll.approve_file(release_request, relpath, request.user) + except bll.ApprovalPermissionDenied as exc: + raise PermissionDenied(str(exc)) + + messages.success(request, "File has been approved") + # TODO: this should go to the previous page (file view?) + return redirect(release_request.get_url()) + + +@instrument(kwarg_attributes={"release_request": "request_id"}) +@require_http_methods(["POST"]) +def file_reject(request, request_id, path: str): + release_request = get_release_request_or_raise(request.user, request_id) + + try: + relpath = release_request.get_request_file(path).relpath + except bll.FileNotFound: + raise Http404() + + try: + bll.reject_file(release_request, relpath, request.user) + except bll.ApprovalPermissionDenied as exc: + raise PermissionDenied(str(exc)) + + # TODO: should this be messages.error() ? + messages.success(request, "File has been rejected") + # TODO: this should go to the previous page (file view?) + return redirect(release_request.get_url()) + + +@instrument(kwarg_attributes={"release_request": "request_id"}) +@require_http_methods(["POST"]) def request_release_files(request, request_id): release_request = get_release_request_or_raise(request.user, request_id) diff --git a/tests/integration/views/test_request.py b/tests/integration/views/test_request.py index 03bec6de4..dda01e488 100644 --- a/tests/integration/views/test_request.py +++ b/tests/integration/views/test_request.py @@ -1,10 +1,12 @@ from io import BytesIO +from pathlib import PurePosixPath as UrlPath import pytest import requests from airlock.business_logic import ( AuditEventType, + FileReviewStatus, RequestFileType, RequestStatus, UrlPath, @@ -376,6 +378,116 @@ def test_request_submit_not_author(airlock_client): assert persisted_request.status == RequestStatus.PENDING +@pytest.mark.parametrize("review", [("approve"), ("reject")]) +def test_file_review_bad_user(airlock_client, review): + workspace = "test1" + airlock_client.login(workspaces=[workspace], output_checker=False) + author = factories.create_user("author", [workspace], False) + release_request = factories.create_release_request( + workspace, + user=author, + status=RequestStatus.SUBMITTED, + ) + path = "path/test.txt" + factories.write_request_file(release_request, "group", path, contents="test") + + response = airlock_client.post( + f"/requests/{review}/{release_request.id}/group/{path}" + ) + assert response.status_code == 403 + relpath = UrlPath(path) + assert ( + len( + factories.bll.get_release_request(release_request.id, author) + .filegroups["group"] + .files[relpath] + .reviews + ) + == 0 + ) + + +@pytest.mark.parametrize("review", [("approve"), ("reject")]) +def test_file_review_bad_file(airlock_client, review): + airlock_client.login(output_checker=True) + author = factories.create_user("author", ["test1"], False) + release_request = factories.create_release_request( + "test1", + user=author, + status=RequestStatus.SUBMITTED, + ) + path = "path/test.txt" + factories.write_request_file(release_request, "group", path, contents="test") + + bad_path = "path/bad.txt" + response = airlock_client.post( + f"/requests/{review}/{release_request.id}/group/{bad_path}" + ) + assert response.status_code == 404 + relpath = UrlPath(path) + assert ( + len( + factories.bll.get_release_request(release_request.id, author) + .filegroups["group"] + .files[relpath] + .reviews + ) + == 0 + ) + + +def test_file_approve(airlock_client): + airlock_client.login(output_checker=True) + author = factories.create_user("author", ["test1"], False) + release_request = factories.create_release_request( + "test1", + user=author, + status=RequestStatus.SUBMITTED, + ) + path = "path/test.txt" + factories.write_request_file(release_request, "group", path, contents="test") + + response = airlock_client.post( + f"/requests/approve/{release_request.id}/group/{path}" + ) + assert response.status_code == 302 + relpath = UrlPath(path) + review = ( + factories.bll.get_release_request(release_request.id, author) + .filegroups["group"] + .files[relpath] + .reviews[0] + ) + assert review.status == FileReviewStatus.APPROVED + assert review.reviewer == "testuser" + + +def test_file_reject(airlock_client): + airlock_client.login(output_checker=True) + author = factories.create_user("author", ["test1"], False) + release_request = factories.create_release_request( + "test1", + user=author, + status=RequestStatus.SUBMITTED, + ) + path = "path/test.txt" + factories.write_request_file(release_request, "group", path, contents="test") + + response = airlock_client.post( + f"/requests/reject/{release_request.id}/group/{path}" + ) + assert response.status_code == 302 + relpath = UrlPath(path) + review = ( + factories.bll.get_release_request(release_request.id, author) + .filegroups["group"] + .files[relpath] + .reviews[0] + ) + assert review.status == FileReviewStatus.REJECTED + assert review.reviewer == "testuser" + + def test_request_reject_output_checker(airlock_client): airlock_client.login(output_checker=True) author = factories.create_user("author", ["test1"], False)