Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions services/task/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,12 @@ def delete_timeseries(self, repository_id: int):
kwargs=dict(repository_id=repository_id),
).apply_async()

def transplant_report(self, repo_id: int, from_sha: str, to_sha: str) -> None:
self._create_signature(
"app.tasks.reports.transplant_report",
kwargs={"repo_id": repo_id, "from_sha": from_sha, "to_sha": to_sha},
).apply_async()

def update_commit(self, commitid, repoid):
self._create_signature(
"app.tasks.commit_update.CommitUpdate",
Expand Down
34 changes: 34 additions & 0 deletions upload/tests/views/test_transplant_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from django.urls import reverse
from rest_framework.test import APIClient
from shared.django_apps.core.tests.factories import RepositoryFactory

from upload.views.uploads import CanDoCoverageUploadsPermission


def test_uploads_get_not_allowed(db, mocker):
mocker.patch.object(
CanDoCoverageUploadsPermission, "has_permission", return_value=True
)
task_mock = mocker.patch("services.task.TaskService.transplant_report")

repository = RepositoryFactory(
name="the-repo", author__username="codecov", author__service="github"
)
owner = repository.author
client = APIClient()
client.force_authenticate(user=owner)

url = reverse(
"new_upload.transplant_report",
args=["github", "codecov::::the-repo"],
)
assert url == "/upload/github/codecov::::the-repo/commits/transplant"

res = client.post(
url, data={"from_sha": "sha to copy from", "to_sha": "sha to copy to"}
)
assert res.status_code == 200

task_mock.assert_called_once_with(
repo_id=repository.repoid, from_sha="sha to copy from", to_sha="sha to copy to"
)
6 changes: 6 additions & 0 deletions upload/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from upload.views.legacy import UploadDownloadHandler, UploadHandler
from upload.views.reports import ReportResultsView, ReportViews
from upload.views.test_results import TestResultsView
from upload.views.transplant_report import TransplantReportView
from upload.views.upload_completion import UploadCompletionView
from upload.views.upload_coverage import UploadCoverageView
from upload.views.uploads import UploadViews
Expand Down Expand Up @@ -58,6 +59,11 @@
CommitViews.as_view(),
name="new_upload.commits",
),
path(
"<str:service>/<str:repo>/commits/transplant",
TransplantReportView.as_view(),
name="new_upload.transplant_report",
),
path(
"<str:service>/<str:repo>/upload-coverage",
UploadCoverageView.as_view(),
Expand Down
69 changes: 69 additions & 0 deletions upload/views/transplant_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import logging
from typing import Any, Callable

from django.http import HttpRequest
from rest_framework import serializers, status
from rest_framework.generics import CreateAPIView
from rest_framework.response import Response
from shared.metrics import inc_counter

from codecov_auth.authentication.repo_auth import (
GitHubOIDCTokenAuthentication,
GlobalTokenAuthentication,
OrgLevelTokenAuthentication,
RepositoryLegacyTokenAuthentication,
UploadTokenRequiredAuthenticationCheck,
repo_auth_custom_exception_handler,
)
from services.task import TaskService
from upload.helpers import generate_upload_prometheus_metrics_labels
from upload.metrics import API_UPLOAD_COUNTER
from upload.views.base import GetterMixin
from upload.views.uploads import CanDoCoverageUploadsPermission

log = logging.getLogger(__name__)


class TransplantReportSerializer(serializers.Serializer):
from_sha = serializers.CharField(required=True)
to_sha = serializers.CharField(required=True)


class TransplantReportView(CreateAPIView, GetterMixin):
permission_classes = [CanDoCoverageUploadsPermission]
authentication_classes = [
UploadTokenRequiredAuthenticationCheck,
GlobalTokenAuthentication,
OrgLevelTokenAuthentication,
GitHubOIDCTokenAuthentication,
RepositoryLegacyTokenAuthentication,
]

def get_exception_handler(self) -> Callable[[Exception, dict[str, Any]], Response]:
return repo_auth_custom_exception_handler

def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> Response:
inc_counter(
API_UPLOAD_COUNTER,
labels=generate_upload_prometheus_metrics_labels(
action="coverage",
endpoint="transplant_report",
request=self.request,
is_shelter_request=self.is_shelter_request(),
),
)
serializer = TransplantReportSerializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

data = serializer.validated_data
TaskService().transplant_report(
repo_id=self.get_repo().repoid,
from_sha=data["from_sha"],
to_sha=data["to_sha"],
)

return Response(
data={"result": "All good, transplant scheduled"},
status=status.HTTP_200_OK,
)