|
46 | 46 | PackageUploadSerializer, |
47 | 47 | PackageUploadTaskSerializer, |
48 | 48 | SummarySerializer, |
| 49 | + YankSerializer, |
49 | 50 | ) |
50 | 51 | from pulp_python.app.utils import ( |
51 | 52 | PYPI_LAST_SERIAL, |
@@ -586,3 +587,70 @@ def retrieve(self, request, path, package, version, filename): |
586 | 587 | if provenance: |
587 | 588 | return Response(data=provenance.provenance) |
588 | 589 | return HttpResponseNotFound(f"{package} {version} {filename} provenance does not exist.") |
| 590 | + |
| 591 | + |
| 592 | +class YankView(PyPIMixin, ViewSet): |
| 593 | + """View for yank/unyank requests (PEP 592).""" |
| 594 | + |
| 595 | + endpoint_name = "yank" |
| 596 | + DEFAULT_ACCESS_POLICY = { |
| 597 | + "statements": [ |
| 598 | + { |
| 599 | + "action": ["yank", "unyank"], |
| 600 | + "principal": "authenticated", |
| 601 | + "effect": "allow", |
| 602 | + "condition": "index_has_repo_perm:python.modify_pythonrepository", |
| 603 | + }, |
| 604 | + ], |
| 605 | + } |
| 606 | + |
| 607 | + @extend_schema(request=YankSerializer, summary="Yank a package version") |
| 608 | + def yank(self, request, path): |
| 609 | + """Yank a package version, marking all its files with data-yanked.""" |
| 610 | + repo = self.distribution.repository |
| 611 | + if not repo: |
| 612 | + return HttpResponseBadRequest(reason="Index is not pointing to a repository") |
| 613 | + |
| 614 | + serializer = YankSerializer(data=request.data) |
| 615 | + serializer.is_valid(raise_exception=True) |
| 616 | + |
| 617 | + normalized = canonicalize_name(serializer.validated_data["name"]) |
| 618 | + version = serializer.validated_data["version"] |
| 619 | + repo_ver = self.get_repository_version(self.distribution) |
| 620 | + if not PythonPackageContent.objects.filter( |
| 621 | + pk__in=repo_ver.content, name_normalized=normalized, version=version |
| 622 | + ).exists(): |
| 623 | + return HttpResponseNotFound(f"{normalized}=={version} not found in repository") |
| 624 | + |
| 625 | + result = dispatch( |
| 626 | + tasks.ayank_package, |
| 627 | + exclusive_resources=[repo], |
| 628 | + kwargs={ |
| 629 | + "repository_pk": str(repo.pk), |
| 630 | + "name": serializer.validated_data["name"], |
| 631 | + "version": serializer.validated_data["version"], |
| 632 | + "yanked_reason": serializer.validated_data.get("yanked_reason", ""), |
| 633 | + }, |
| 634 | + ) |
| 635 | + return OperationPostponedResponse(result, request) |
| 636 | + |
| 637 | + @extend_schema(request=YankSerializer, summary="Unyank a package version") |
| 638 | + def unyank(self, request, path): |
| 639 | + """Unyank a package version, unmarking all its files with data-yanked.""" |
| 640 | + repo = self.distribution.repository |
| 641 | + if not repo: |
| 642 | + return HttpResponseBadRequest(reason="Index is not pointing to a repository") |
| 643 | + |
| 644 | + serializer = YankSerializer(data=request.data) |
| 645 | + serializer.is_valid(raise_exception=True) |
| 646 | + |
| 647 | + result = dispatch( |
| 648 | + tasks.aunyank_package, |
| 649 | + exclusive_resources=[repo], |
| 650 | + kwargs={ |
| 651 | + "repository_pk": str(repo.pk), |
| 652 | + "name": serializer.validated_data["name"], |
| 653 | + "version": serializer.validated_data["version"], |
| 654 | + }, |
| 655 | + ) |
| 656 | + return OperationPostponedResponse(result, request) |
0 commit comments