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 @@ -3,7 +3,10 @@ # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information +from __future__ import annotations + import enum +from typing import List from django.db import models @@ -30,6 +33,30 @@ def choices(cls): return tuple((variant.name, variant.value) for variant in cls) + def allowed_next_statuses(self) -> List[RequestStatus]: + next_statuses = { + self.PENDING: [self.WAITING_FOR_FEEDBACK, self.REJECTED, self.SUSPENDED], + self.WAITING_FOR_FEEDBACK: [self.FEEDBACK_TO_HANDLE], + self.FEEDBACK_TO_HANDLE: [ + self.WAITING_FOR_FEEDBACK, + self.ACCEPTED, + self.REJECTED, + self.SUSPENDED, + ], + self.ACCEPTED: [self.SCHEDULED], + self.SCHEDULED: [ + self.FIRST_LISTING_DONE, + # in case of race condition between lister and loader: + self.FIRST_ORIGIN_LOADED, + ], + self.FIRST_LISTING_DONE: [self.FIRST_ORIGIN_LOADED], + self.FIRST_ORIGIN_LOADED: [], + self.REJECTED: [], + self.SUSPENDED: [self.PENDING], + self.DENIED: [], + } + return next_statuses[self] # type: ignore + class RequestActorRole(enum.Enum): MODERATOR = "moderator" 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 @@ -7,7 +7,9 @@ from typing import Union from django.core.exceptions import ObjectDoesNotExist -from django.forms import ModelForm +from django.db import transaction +from django.forms import CharField, ModelForm +from django.http import HttpResponseBadRequest from django.http.request import HttpRequest from django.http.response import HttpResponse, HttpResponseForbidden from rest_framework import serializers @@ -15,10 +17,21 @@ from rest_framework.response import Response from swh.web.add_forge_now.models import Request as AddForgeRequest +from swh.web.add_forge_now.models import RequestActorRole as AddForgeNowRequestActorRole +from swh.web.add_forge_now.models import RequestHistory as AddForgeNowRequestHistory +from swh.web.add_forge_now.models import RequestStatus as AddForgeNowRequestStatus from swh.web.api.apidoc import api_doc, format_docstring from swh.web.api.apiurls import api_route from swh.web.common.exc import BadInputExc +MODERATOR_ROLE = "swh.web.add_forge_now.moderator" + + +def _block_while_testing(): + """Replaced by tests to check concurrency behavior + """ + pass + class AddForgeNowRequestForm(ModelForm): class Meta: @@ -32,12 +45,29 @@ ) +class AddForgeNowRequestHistoryForm(ModelForm): + new_status = CharField(max_length=200, required=False,) + + class Meta: + model = AddForgeNowRequestHistory + fields = ("text", "new_status") + + class AddForgeNowRequestSerializer(serializers.ModelSerializer): class Meta: model = AddForgeRequest fields = "__all__" +class AddForgeNowRequestPublicSerializer(serializers.ModelSerializer): + """Serializes AddForgeRequest without private fields. + """ + + class Meta: + model = AddForgeRequest + fields = ("forge_url", "forge_type", "status", "submission_date") + + @api_route( r"/add-forge/request/create", "api-1-add-forge-request-create", methods=["POST"], ) @@ -106,3 +136,91 @@ data = AddForgeNowRequestSerializer(add_forge_request).data return Response(data=data, status=201) + + +@api_route( + r"/add-forge/request/(?P[0-9]+)/update/", + "api-1-add-forge-request-update", + methods=["POST"], +) +@api_doc("/add-forge/request/update", tags=["hidden"]) +@format_docstring() +@transaction.atomic +def api_add_forge_request_update( + request: Union[HttpRequest, Request], id: int +) -> HttpResponse: + """ + .. http:post:: /api/1/add-forge/request/update/ + + Update a request to add a forge to the list of those crawled regularly + by Software Heritage. + + .. warning:: + That endpoint is not publicly available and requires authentication + in order to be able to request it. + + {common_headers} + + :