diff --git a/swh/web/add_forge_now/models.py b/swh/web/add_forge_now/models.py --- a/swh/web/add_forge_now/models.py +++ b/swh/web/add_forge_now/models.py @@ -61,6 +61,7 @@ class RequestActorRole(enum.Enum): MODERATOR = "moderator" SUBMITTER = "submitter" + FORGE_ADMIN = "forge admin" @classmethod def choices(cls): diff --git a/swh/web/api/views/add_forge_now.py b/swh/web/api/views/add_forge_now.py --- a/swh/web/api/views/add_forge_now.py +++ b/swh/web/api/views/add_forge_now.py @@ -67,7 +67,19 @@ class Meta: model = AddForgeRequest - fields = ("forge_url", "forge_type", "status", "submission_date") + fields = ("id", "forge_url", "forge_type", "status", "submission_date") + + +class AddForgeNowRequestHistorySerializer(serializers.ModelSerializer): + class Meta: + model = AddForgeNowRequestHistory + exclude = ("request",) + + +class AddForgeNowRequestHistoryPublicSerializer(serializers.ModelSerializer): + class Meta: + model = AddForgeNowRequestHistory + fields = ("id", "date", "new_status", "actor_role") @api_route( @@ -75,6 +87,7 @@ ) @api_doc("/add-forge/request/create") @format_docstring() +@transaction.atomic def api_add_forge_request_create(request: Union[HttpRequest, Request]) -> HttpResponse: """ .. http:post:: /api/1/add-forge/request/create/ @@ -135,6 +148,13 @@ form.save() + request_history = AddForgeNowRequestHistory() + request_history.request = add_forge_request + request_history.new_status = AddForgeNowRequestStatus.PENDING.name + request_history.actor = request.user.username + request_history.actor_role = AddForgeNowRequestActorRole.SUBMITTER.name + request_history.save() + data = AddForgeNowRequestSerializer(add_forge_request).data return Response(data=data, status=201) @@ -288,3 +308,45 @@ ) return response + + +@api_route( + r"/add-forge/request/(?P[0-9]+)/get", + "api-1-add-forge-request-get", + methods=["GET"], +) +@api_doc("/add-forge/request/get") +@format_docstring() +def api_add_forge_request_get(request: Request, id: int): + """ + .. http:get:: /api/1/add-forge/request/get/ + + Return all details about an add-forge request. + + {common_headers} + + :param int id: add-forge request identifier + + :statuscode 200: request details successfully returned + :statuscode 400: request identifier does not exist + """ + + try: + add_forge_request = AddForgeRequest.objects.get(id=id) + except ObjectDoesNotExist: + raise BadInputExc("Request id does not exist") + + request_history = AddForgeNowRequestHistory.objects.filter( + request=add_forge_request + ).order_by("id") + + if request.user.is_authenticated and request.user.has_perm(MODERATOR_ROLE): + data = AddForgeNowRequestSerializer(add_forge_request).data + history = AddForgeNowRequestHistorySerializer(request_history, many=True).data + else: + data = AddForgeNowRequestPublicSerializer(add_forge_request).data + history = AddForgeNowRequestHistoryPublicSerializer( + request_history, many=True + ).data + + return {"request": data, "history": history} diff --git a/swh/web/tests/api/views/test_add_forge_now.py b/swh/web/tests/api/views/test_add_forge_now.py --- a/swh/web/tests/api/views/test_add_forge_now.py +++ b/swh/web/tests/api/views/test_add_forge_now.py @@ -22,6 +22,7 @@ ) +@pytest.mark.django_db def test_add_forge_request_create_anonymous_user(api_client): url = reverse("api-1-add-forge-request-create") check_api_post_response(api_client, url, status_code=403) @@ -160,9 +161,7 @@ def _create_add_forge_request(api_client, regular_user, data=ADD_FORGE_DATA): api_client.force_login(regular_user) url = reverse("api-1-add-forge-request-create") - check_api_post_response( - api_client, url, data=data, status_code=201, - ) + return check_api_post_response(api_client, url, data=data, status_code=201,) @pytest.mark.django_db(transaction=True, reset_sequences=True) @@ -285,6 +284,7 @@ "forge_type": ADD_FORGE_DATA["forge_type"], "status": "PENDING", "submission_date": resp.data[0]["submission_date"], + "id": 1, } assert resp.data == [add_forge_request] @@ -298,6 +298,7 @@ "forge_type": ADD_OTHER_FORGE_DATA["forge_type"], "status": "PENDING", "submission_date": resp.data[0]["submission_date"], + "id": 2, } assert resp.data == [other_forge_request, add_forge_request] @@ -384,3 +385,105 @@ resp = check_api_get_responses(api_client, url, status_code=200) assert len(resp.data) == 1 + + +@pytest.mark.django_db(transaction=True, reset_sequences=True) +def test_add_forge_request_get(api_client, regular_user, moderator_user): + resp = _create_add_forge_request(api_client, regular_user) + + submission_date = resp.data["submission_date"] + + url = reverse("api-1-add-forge-request-update", url_args={"id": 1}) + + api_client.force_login(moderator_user) + check_api_post_response( + api_client, + url, + data={"new_status": "WAITING_FOR_FEEDBACK", "text": "waiting for message"}, + status_code=200, + ) + api_client.logout() + + url = reverse("api-1-add-forge-request-get", url_args={"id": 1}) + + resp = check_api_get_responses(api_client, url, status_code=200) + + assert resp.data == { + "request": { + "forge_url": ADD_FORGE_DATA["forge_url"], + "forge_type": ADD_FORGE_DATA["forge_type"], + "id": 1, + "status": "WAITING_FOR_FEEDBACK", + "submission_date": submission_date, + }, + "history": [ + { + "id": 1, + "actor_role": "SUBMITTER", + "date": resp.data["history"][0]["date"], + "new_status": "PENDING", + }, + { + "id": 2, + "actor_role": "MODERATOR", + "date": resp.data["history"][1]["date"], + "new_status": "WAITING_FOR_FEEDBACK", + }, + ], + } + + +@pytest.mark.django_db(transaction=True, reset_sequences=True) +def test_add_forge_request_get_moderator(api_client, regular_user, moderator_user): + resp = _create_add_forge_request(api_client, regular_user) + + submission_date = resp.data["submission_date"] + + url = reverse("api-1-add-forge-request-update", url_args={"id": 1}) + + api_client.force_login(moderator_user) + check_api_post_response( + api_client, + url, + data={"new_status": "WAITING_FOR_FEEDBACK", "text": "waiting for message"}, + status_code=200, + ) + + url = reverse("api-1-add-forge-request-get", url_args={"id": 1}) + + resp = check_api_get_responses(api_client, url, status_code=200) + + assert resp.data == { + "request": { + **ADD_FORGE_DATA, + "id": 1, + "status": "WAITING_FOR_FEEDBACK", + "submission_date": submission_date, + "submitter_name": regular_user.username, + "submitter_email": regular_user.email, + }, + "history": [ + { + "id": 1, + "text": "", + "actor": regular_user.username, + "actor_role": "SUBMITTER", + "date": resp.data["history"][0]["date"], + "new_status": "PENDING", + }, + { + "id": 2, + "text": "waiting for message", + "actor": moderator_user.username, + "actor_role": "MODERATOR", + "date": resp.data["history"][1]["date"], + "new_status": "WAITING_FOR_FEEDBACK", + }, + ], + } + + +@pytest.mark.django_db(transaction=True, reset_sequences=True) +def test_add_forge_request_get_invalid(api_client): + url = reverse("api-1-add-forge-request-get", url_args={"id": 3}) + check_api_get_responses(api_client, url, status_code=400)