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 @@ -4,11 +4,12 @@ # See top-level LICENSE file for more information import json -from typing import Union +from typing import Any, Dict, Union from django.core.exceptions import ObjectDoesNotExist from django.core.paginator import Paginator from django.db import transaction +from django.db.models import Q from django.forms import CharField, ModelForm from django.http import HttpResponseBadRequest from django.http.request import HttpRequest @@ -269,7 +270,45 @@ :statuscode 200: always """ - add_forge_requests = AddForgeRequest.objects.order_by("-id") + datatables = int(request.GET.get("draw", 0)) + + if datatables: + # datatables request handling + add_forge_requests = AddForgeRequest.objects.all() + + table_data: Dict[str, Any] = {} + table_data["recordsTotal"] = add_forge_requests.count() + table_data["draw"] = int(request.GET["draw"]) + + search_value = request.GET.get("search[value]") + + column_order = request.GET.get("order[0][column]") + field_order = request.GET.get(f"columns[{column_order}][name]") + order_dir = request.GET.get("order[0][dir]") + + if field_order: + if order_dir == "desc": + field_order = "-" + field_order + add_forge_requests = add_forge_requests.order_by(field_order) + else: + add_forge_requests = add_forge_requests.order_by("-id") + + per_page = int(request.GET["length"]) + page_num = int(request.GET["start"]) // per_page + 1 + + if search_value: + add_forge_requests = add_forge_requests.filter( + Q(forge_type__icontains=search_value) + | Q(forge_url__icontains=search_value) + | Q(status__icontains=search_value) + ) + else: + # standard Web API request handling + add_forge_requests = AddForgeRequest.objects.order_by("-id") + + page_num = int(request.GET.get("page", 1)) + per_page = int(request.GET.get("per_page", 10)) + per_page = min(per_page, 1000) if ( int(request.GET.get("user_requests_only", "0")) @@ -279,35 +318,43 @@ submitter_name=request.user.username ) - page_num = int(request.GET.get("page", 1)) - per_page = int(request.GET.get("per_page", 10)) - per_page = min(per_page, 1000) - paginator = Paginator(add_forge_requests, per_page) page = paginator.page(page_num) if request.user.has_perm(MODERATOR_ROLE): - results = AddForgeNowRequestSerializer(page.object_list, many=True).data + requests = AddForgeNowRequestSerializer(page.object_list, many=True).data else: - results = AddForgeNowRequestPublicSerializer(page.object_list, many=True).data + requests = AddForgeNowRequestPublicSerializer(page.object_list, many=True).data - response = {"results": results, "headers": {}} + results = [dict(request) for request in requests] - if page.has_previous(): - response["headers"]["link-prev"] = reverse( - "api-1-add-forge-request-list", - query_params={"page": page.previous_page_number(), "per_page": per_page}, - request=request, - ) + if datatables: + # datatables response + table_data["recordsFiltered"] = add_forge_requests.count() + table_data["data"] = results + return table_data + else: + # standard Web API response + response: Dict[str, Any] = {"results": results, "headers": {}} + + if page.has_previous(): + response["headers"]["link-prev"] = reverse( + "api-1-add-forge-request-list", + query_params={ + "page": page.previous_page_number(), + "per_page": per_page, + }, + request=request, + ) - if page.has_next(): - response["headers"]["link-next"] = reverse( - "api-1-add-forge-request-list", - query_params={"page": page.next_page_number(), "per_page": per_page}, - request=request, - ) + if page.has_next(): + response["headers"]["link-next"] = reverse( + "api-1-add-forge-request-list", + query_params={"page": page.next_page_number(), "per_page": per_page}, + request=request, + ) - return response + return response @api_route( 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 @@ -387,6 +387,150 @@ assert len(resp.data) == 1 +NB_FORGE_TYPE = 2 +NB_FORGES_PER_TYPE = 20 + + +def _create_add_forge_requests(api_client, regular_user, regular_user2): + requests = [] + for i in range(NB_FORGES_PER_TYPE): + request = { + "forge_type": "gitlab", + "forge_url": f"https://gitlab.example{i:02d}.org", + "forge_contact_email": f"admin@gitlab.example{i:02d}.org", + "forge_contact_name": f"gitlab.example{i:02d}.org admin", + "forge_contact_comment": "user marked as owner in forge members", + } + _create_add_forge_request( + api_client, regular_user, data=request, + ) + requests.append(request) + + request = { + "forge_type": "gitea", + "forge_url": f"https://gitea.example{i:02d}.org", + "forge_contact_email": f"admin@gitea.example{i:02d}.org", + "forge_contact_name": f"gitea.example{i:02d}.org admin", + "forge_contact_comment": "user marked as owner in forge members", + } + _create_add_forge_request( + api_client, regular_user2, data=request, + ) + requests.append(request) + return requests + + +@pytest.mark.django_db(transaction=True, reset_sequences=True) +def test_add_forge_request_list_datatables( + api_client, regular_user, regular_user2, moderator_user +): + _create_add_forge_requests(api_client, regular_user, regular_user2) + + length = 10 + + url = reverse( + "api-1-add-forge-request-list", + query_params={"draw": 1, "length": length, "start": 0}, + ) + + api_client.force_login(regular_user) + resp = check_api_get_responses(api_client, url, status_code=200) + + assert resp.data["draw"] == 1 + assert resp.data["recordsFiltered"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE + assert resp.data["recordsTotal"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE + assert len(resp.data["data"]) == length + # default ordering is by descending id + assert resp.data["data"][0]["id"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE + assert ( + resp.data["data"][-1]["id"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE - length + 1 + ) + assert "submitter_name" not in resp.data["data"][0] + + api_client.force_login(moderator_user) + resp = check_api_get_responses(api_client, url, status_code=200) + + assert resp.data["draw"] == 1 + assert resp.data["recordsFiltered"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE + assert resp.data["recordsTotal"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE + assert len(resp.data["data"]) == length + # default ordering is by descending id + assert resp.data["data"][0]["id"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE + assert ( + resp.data["data"][-1]["id"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE - length + 1 + ) + assert "submitter_name" in resp.data["data"][0] + + +@pytest.mark.django_db(transaction=True, reset_sequences=True) +def test_add_forge_request_list_datatables_ordering( + api_client, regular_user, regular_user2, moderator_user +): + requests = _create_add_forge_requests(api_client, regular_user, regular_user2) + requests_sorted = list(sorted(requests, key=lambda d: d["forge_url"])) + forge_urls_asc = [request["forge_url"] for request in requests_sorted] + forge_urls_desc = list(reversed(forge_urls_asc)) + + length = 10 + + for direction in ("asc", "desc"): + for i in range(4): + url = reverse( + "api-1-add-forge-request-list", + query_params={ + "draw": 1, + "length": length, + "start": i * length, + "order[0][column]": 2, + "order[0][dir]": direction, + "columns[2][name]": "forge_url", + }, + ) + + api_client.force_login(regular_user) + resp = check_api_get_responses(api_client, url, status_code=200) + + assert resp.data["draw"] == 1 + assert resp.data["recordsFiltered"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE + assert resp.data["recordsTotal"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE + assert len(resp.data["data"]) == length + + page_forge_urls = [request["forge_url"] for request in resp.data["data"]] + if direction == "asc": + expected_forge_urls = forge_urls_asc[i * length : (i + 1) * length] + else: + expected_forge_urls = forge_urls_desc[i * length : (i + 1) * length] + assert page_forge_urls == expected_forge_urls + + +@pytest.mark.django_db(transaction=True, reset_sequences=True) +def test_add_forge_request_list_datatables_search( + api_client, regular_user, regular_user2, moderator_user +): + _create_add_forge_requests(api_client, regular_user, regular_user2) + + url = reverse( + "api-1-add-forge-request-list", + query_params={ + "draw": 1, + "length": NB_FORGES_PER_TYPE, + "start": 0, + "search[value]": "gitlab", + }, + ) + + api_client.force_login(regular_user) + resp = check_api_get_responses(api_client, url, status_code=200) + + assert resp.data["draw"] == 1 + assert resp.data["recordsFiltered"] == NB_FORGES_PER_TYPE + assert resp.data["recordsTotal"] == NB_FORGE_TYPE * NB_FORGES_PER_TYPE + assert len(resp.data["data"]) == NB_FORGES_PER_TYPE + + page_forge_type = [request["forge_type"] for request in resp.data["data"]] + assert page_forge_type == ["gitlab"] * NB_FORGES_PER_TYPE + + @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)