Repository: pulp/pulpcore

Issue:
Enable file-less "uploads"
**Is your feature request related to a problem? Please describe.**

Right now pulpcore knows artifactless types, that can be created via file upload using the `NoArtifactContentUploadViewSet` and the `NoArtifactContentUploadSerializer`, which can be combined with "retrieve behaviour" (do not throw errors if the requested content already esists/is already in the repo it should be added to) by defining a plugin specific `retrieve` function on the serializer.

However, pulp_deb has several artifact less types, that do not need an actual uploaded file as part of this process at all. All they need (for pulp_deb to be able to create them) is the set of required API parameters. Examples include the `ReleaseComponent` and `ReleaseArchitecture`. These content types should still use the `repository` parameter to create and add them to a repository in one action, along with "retrieve behaviour". Since this means creating new repository versions, this action must be performed as a task to ensure resource locks.

As far as I can tell this is currently not possible, because pulpcore does not have the right kind of `ViewSet`. I was able to get things to work with the following adjustments to the `NoArtifactContentUploadViewSet`: https://github.com/pulp/pulpcore/pull/5084

An alternative might be to split up `NoArtifactContentUploadViewSet` into `NoArtifactContentUploadViewSet` and `NoArtifactContentViewSet`, which would mirror the class structure on the serializer side, and possibly make the semantic intention more clear.

**Additional context**

- See here for the pulp_deb change that prompted this need: https://github.com/pulp/pulp_deb/pull/1018
- I am happy to implement this as soon as there is a consensus on whether to add a whole new ViewSet in the class hierarchy, or whether it is enough to adjust `NoArtifactContentUploadViewSet` to support both use cases.
- I could use help in designing a good test to cover this special use case.



Discussion:
I am feeling a bit of time pressure with respect to this change, because I ideally need it to propagate into Katello within a reasonable time frame.

Resolving patch:
diff --git a/pulpcore/plugin/viewsets/__init__.py b/pulpcore/plugin/viewsets/__init__.py
--- a/pulpcore/plugin/viewsets/__init__.py
+++ b/pulpcore/plugin/viewsets/__init__.py
@@ -44,6 +44,7 @@
 from pulpcore.filters import HyperlinkRelatedFilter
 
 from .content import (
+    NoArtifactContentViewSet,
     NoArtifactContentUploadViewSet,
     SingleArtifactContentUploadViewSet,
 )
diff --git a/pulpcore/plugin/viewsets/content.py b/pulpcore/plugin/viewsets/content.py
--- a/pulpcore/plugin/viewsets/content.py
+++ b/pulpcore/plugin/viewsets/content.py
@@ -29,6 +29,35 @@ def get_deferred_context(self, request):
         return {}
 
 
+class NoArtifactContentViewSet(DefaultDeferredContextMixin, ContentViewSet):
+    """A ViewSet for content creation that does not require a file to be uploaded."""
+
+    @extend_schema(
+        description="Trigger an asynchronous task to create content,"
+        "optionally create new repository version.",
+        responses={202: AsyncOperationResponseSerializer},
+    )
+    def create(self, request):
+        """Create a content unit."""
+        serializer = self.get_serializer(data=request.data)
+        serializer.is_valid(raise_exception=True)
+
+        exclusive_resources = [
+            item for item in (serializer.validated_data.get(key) for key in ("repository",)) if item
+        ]
+
+        task = dispatch(
+            tasks.base.general_create,
+            exclusive_resources=exclusive_resources,
+            args=(self.queryset.model._meta.app_label, serializer.__class__.__name__),
+            kwargs={
+                "data": {k: v for k, v in request.data.items()},
+                "context": self.get_deferred_context(request),
+            },
+        )
+        return OperationPostponedResponse(task, request)
+
+
 class NoArtifactContentUploadViewSet(DefaultDeferredContextMixin, ContentViewSet):
     """A ViewSet for uploads that do not require to store an uploaded content as an Artifact."""
 


Test patch: