Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit 600412a

Browse files
authored
Add a new transplant endpoint (#1276)
1 parent 1b1e320 commit 600412a

File tree

4 files changed

+115
-0
lines changed

4 files changed

+115
-0
lines changed

services/task/task.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,12 @@ def delete_timeseries(self, repository_id: int):
309309
kwargs=dict(repository_id=repository_id),
310310
).apply_async()
311311

312+
def transplant_report(self, repo_id: int, from_sha: str, to_sha: str) -> None:
313+
self._create_signature(
314+
"app.tasks.reports.transplant_report",
315+
kwargs={"repo_id": repo_id, "from_sha": from_sha, "to_sha": to_sha},
316+
).apply_async()
317+
312318
def update_commit(self, commitid, repoid):
313319
self._create_signature(
314320
"app.tasks.commit_update.CommitUpdate",
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from django.urls import reverse
2+
from rest_framework.test import APIClient
3+
from shared.django_apps.core.tests.factories import RepositoryFactory
4+
5+
from upload.views.uploads import CanDoCoverageUploadsPermission
6+
7+
8+
def test_uploads_get_not_allowed(db, mocker):
9+
mocker.patch.object(
10+
CanDoCoverageUploadsPermission, "has_permission", return_value=True
11+
)
12+
task_mock = mocker.patch("services.task.TaskService.transplant_report")
13+
14+
repository = RepositoryFactory(
15+
name="the-repo", author__username="codecov", author__service="github"
16+
)
17+
owner = repository.author
18+
client = APIClient()
19+
client.force_authenticate(user=owner)
20+
21+
url = reverse(
22+
"new_upload.transplant_report",
23+
args=["github", "codecov::::the-repo"],
24+
)
25+
assert url == "/upload/github/codecov::::the-repo/commits/transplant"
26+
27+
res = client.post(
28+
url, data={"from_sha": "sha to copy from", "to_sha": "sha to copy to"}
29+
)
30+
assert res.status_code == 200
31+
32+
task_mock.assert_called_once_with(
33+
repo_id=repository.repoid, from_sha="sha to copy from", to_sha="sha to copy to"
34+
)

upload/urls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from upload.views.legacy import UploadDownloadHandler, UploadHandler
77
from upload.views.reports import ReportResultsView, ReportViews
88
from upload.views.test_results import TestResultsView
9+
from upload.views.transplant_report import TransplantReportView
910
from upload.views.upload_completion import UploadCompletionView
1011
from upload.views.upload_coverage import UploadCoverageView
1112
from upload.views.uploads import UploadViews
@@ -58,6 +59,11 @@
5859
CommitViews.as_view(),
5960
name="new_upload.commits",
6061
),
62+
path(
63+
"<str:service>/<str:repo>/commits/transplant",
64+
TransplantReportView.as_view(),
65+
name="new_upload.transplant_report",
66+
),
6167
path(
6268
"<str:service>/<str:repo>/upload-coverage",
6369
UploadCoverageView.as_view(),

upload/views/transplant_report.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import logging
2+
from typing import Any, Callable
3+
4+
from django.http import HttpRequest
5+
from rest_framework import serializers, status
6+
from rest_framework.generics import CreateAPIView
7+
from rest_framework.response import Response
8+
from shared.metrics import inc_counter
9+
10+
from codecov_auth.authentication.repo_auth import (
11+
GitHubOIDCTokenAuthentication,
12+
GlobalTokenAuthentication,
13+
OrgLevelTokenAuthentication,
14+
RepositoryLegacyTokenAuthentication,
15+
UploadTokenRequiredAuthenticationCheck,
16+
repo_auth_custom_exception_handler,
17+
)
18+
from services.task import TaskService
19+
from upload.helpers import generate_upload_prometheus_metrics_labels
20+
from upload.metrics import API_UPLOAD_COUNTER
21+
from upload.views.base import GetterMixin
22+
from upload.views.uploads import CanDoCoverageUploadsPermission
23+
24+
log = logging.getLogger(__name__)
25+
26+
27+
class TransplantReportSerializer(serializers.Serializer):
28+
from_sha = serializers.CharField(required=True)
29+
to_sha = serializers.CharField(required=True)
30+
31+
32+
class TransplantReportView(CreateAPIView, GetterMixin):
33+
permission_classes = [CanDoCoverageUploadsPermission]
34+
authentication_classes = [
35+
UploadTokenRequiredAuthenticationCheck,
36+
GlobalTokenAuthentication,
37+
OrgLevelTokenAuthentication,
38+
GitHubOIDCTokenAuthentication,
39+
RepositoryLegacyTokenAuthentication,
40+
]
41+
42+
def get_exception_handler(self) -> Callable[[Exception, dict[str, Any]], Response]:
43+
return repo_auth_custom_exception_handler
44+
45+
def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> Response:
46+
inc_counter(
47+
API_UPLOAD_COUNTER,
48+
labels=generate_upload_prometheus_metrics_labels(
49+
action="coverage",
50+
endpoint="transplant_report",
51+
request=self.request,
52+
is_shelter_request=self.is_shelter_request(),
53+
),
54+
)
55+
serializer = TransplantReportSerializer(data=request.data)
56+
if not serializer.is_valid():
57+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
58+
59+
data = serializer.validated_data
60+
TaskService().transplant_report(
61+
repo_id=self.get_repo().repoid,
62+
from_sha=data["from_sha"],
63+
to_sha=data["to_sha"],
64+
)
65+
66+
return Response(
67+
data={"result": "All good, transplant scheduled"},
68+
status=status.HTTP_200_OK,
69+
)

0 commit comments

Comments
 (0)