Skip to content

Commit

Permalink
Merge pull request #276 from ATIX-AG/upload_create_serializer
Browse files Browse the repository at this point in the history
File upload
  • Loading branch information
bmbouter authored Sep 27, 2019
2 parents 36f4b95 + 4a64190 commit 4c48d6b
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGES/5403.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add upload functionality to the file content endpoint.
19 changes: 11 additions & 8 deletions pulp_file/app/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@
PublicationDistributionSerializer,
PublicationSerializer,
RemoteSerializer,
SingleArtifactContentSerializer,
SingleArtifactContentUploadSerializer,
)

from .models import FileContent, FileDistribution, FileRemote, FilePublication


class FileContentSerializer(SingleArtifactContentSerializer, ContentChecksumSerializer):
class FileContentSerializer(SingleArtifactContentUploadSerializer, ContentChecksumSerializer):
"""
Serializer for File Content.
"""

def validate(self, data):
def deferred_validate(self, data):
"""Validate the FileContent data."""
data = super().validate(data)
data = super().deferred_validate(data)

data["digest"] = data["artifact"].sha256

Expand All @@ -33,15 +33,18 @@ def validate(self, data):
if content.exists():
raise serializers.ValidationError(
_(
"There is already a file content with relative path '{path}' and artifact "
"'{artifact}'."
).format(path=data["relative_path"], artifact=self.initial_data["artifact"])
"There is already a file content with relative path '{path}' and digest "
"'{digest}'."
).format(path=data["relative_path"], digest=data["digest"])
)

return data

class Meta:
fields = SingleArtifactContentSerializer.Meta.fields + ContentChecksumSerializer.Meta.fields
fields = (
SingleArtifactContentUploadSerializer.Meta.fields
+ ContentChecksumSerializer.Meta.fields
)
model = FileContent


Expand Down
4 changes: 2 additions & 2 deletions pulp_file/app/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
from pulpcore.plugin.tasking import enqueue_with_reservation
from pulpcore.plugin.viewsets import (
BaseDistributionViewSet,
ContentViewSet,
ContentFilter,
RemoteViewSet,
OperationPostponedResponse,
PublicationViewSet,
SingleArtifactContentUploadViewSet,
)

from . import tasks
Expand All @@ -37,7 +37,7 @@ class Meta:
fields = ["relative_path", "digest"]


class FileContentViewSet(ContentViewSet):
class FileContentViewSet(SingleArtifactContentUploadViewSet):
"""
<!-- User-facing documentation, rendered as html-->
FileContent represents a single file and its metadata, which can be added and removed from
Expand Down
90 changes: 83 additions & 7 deletions pulp_file/tests/functional/api/test_crud_content_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
from requests.exceptions import HTTPError

from pulp_smash import api, config, utils
from pulp_smash.exceptions import TaskReportError
from pulp_smash.pulp3.constants import ARTIFACTS_PATH
from pulp_smash.pulp3.utils import delete_orphans

from pulp_file.tests.functional.constants import FILE_CONTENT_PATH, FILE_URL
from pulp_file.tests.functional.utils import gen_file_content_attrs, skip_if
from pulp_file.tests.functional.utils import (
gen_file_content_attrs,
gen_file_content_upload_attrs,
skip_if,
)
from pulp_file.tests.functional.utils import set_up_module as setUpModule # noqa:F401


Expand Down Expand Up @@ -41,7 +46,9 @@ def tearDownClass(cls):
def test_01_create_content_unit(self):
"""Create content unit."""
attrs = gen_file_content_attrs(self.artifact)
self.content_unit.update(self.client.post(FILE_CONTENT_PATH, attrs))
call_report = self.client.post(FILE_CONTENT_PATH, data=attrs)
created_resources = next(api.poll_spawned_tasks(self.cfg, call_report))["created_resources"]
self.content_unit.update(self.client.get(created_resources[0]))
for key, val in attrs.items():
with self.subTest(key=key):
self.assertEqual(self.content_unit[key], val)
Expand Down Expand Up @@ -98,6 +105,74 @@ def test_04_delete(self):
self.assertEqual(exc.exception.response.status_code, 405)


class ContentUnitUploadTestCase(unittest.TestCase):
"""CRUD content unit with upload feature.
This test targets the following issue:
`Pulp #5403 <https://pulp.plan.io/issues/5403>`_
"""

@classmethod
def setUpClass(cls):
"""Create class-wide variable."""
cls.cfg = config.get_config()
delete_orphans(cls.cfg)
cls.content_unit = {}
cls.client = api.Client(cls.cfg, api.smart_handler)
cls.files = {"file": utils.http_get(FILE_URL)}
cls.attrs = gen_file_content_upload_attrs()

@classmethod
def tearDownClass(cls):
"""Clean class-wide variable."""
delete_orphans(cls.cfg)

def test_01_create_content_unit(self):
"""Create content unit."""
content_unit = self.client.post(FILE_CONTENT_PATH, data=self.attrs, files=self.files)
self.content_unit.update(content_unit)
for key, val in self.attrs.items():
with self.subTest(key=key):
self.assertEqual(self.content_unit[key], val)

@skip_if(bool, "content_unit", False)
def test_02_read_content_unit(self):
"""Read a content unit by its href."""
content_unit = self.client.get(self.content_unit["_href"])
for key, val in self.content_unit.items():
with self.subTest(key=key):
self.assertEqual(content_unit[key], val)

@skip_if(bool, "content_unit", False)
def test_02_read_content_units(self):
"""Read a content unit by its relative_path."""
page = self.client.using_handler(api.json_handler).get(
FILE_CONTENT_PATH, params={"relative_path": self.content_unit["relative_path"]}
)
self.assertEqual(len(page["results"]), 1)
for key, val in self.content_unit.items():
with self.subTest(key=key):
self.assertEqual(page["results"][0][key], val)

@skip_if(bool, "content_unit", False)
def test_03_fail_duplicate_content_unit(self):
"""Create content unit."""
with self.assertRaises(TaskReportError) as exc:
self.client.post(FILE_CONTENT_PATH, data=self.attrs, files=self.files)
self.assertEqual(exc.exception.task["state"], "failed")
error = exc.exception.task["error"]
for key in ("already", "relative", "path", "digest"):
self.assertIn(key, error["description"].lower(), error)

@skip_if(bool, "content_unit", False)
def test_03_duplicate_content_unit(self):
"""Create content unit."""
attrs = self.attrs.copy()
attrs["relative_path"] = utils.uuid4()
self.client.post(FILE_CONTENT_PATH, data=attrs, files=self.files)


class DuplicateContentUnit(unittest.TestCase):
"""Attempt to create a duplicate content unit.
Expand Down Expand Up @@ -134,11 +209,12 @@ def test_raise_error(self):
self.client.post(FILE_CONTENT_PATH, attrs)

# using the same attrs used to create the first content unit.
response = api.Client(self.cfg, api.echo_handler).post(FILE_CONTENT_PATH, attrs)
with self.assertRaises(HTTPError):
response.raise_for_status()
for key in ("already", "content", "relative", "path", "artifact"):
self.assertIn(key, response.json()["non_field_errors"][0].lower(), response.json())
with self.assertRaises(TaskReportError) as exc:
self.client.post(FILE_CONTENT_PATH, attrs)
self.assertEqual(exc.exception.task["state"], "failed")
error = exc.exception.task["error"]
for key in ("already", "relative", "path", "digest"):
self.assertIn(key, error["description"].lower(), error)

def test_non_error(self):
"""Create a duplicate content unit with different relative_path.
Expand Down
9 changes: 9 additions & 0 deletions pulp_file/tests/functional/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ def gen_file_content_attrs(artifact):
return {"artifact": artifact["_href"], "relative_path": utils.uuid4()}


def gen_file_content_upload_attrs():
"""Generate a dict with content unit attributes without artifact for upload.
:param artifact: A dict of info about the artifact.
:returns: A semi-random dict for use in creating a content unit.
"""
return {"relative_path": utils.uuid4()}


def populate_pulp(cfg, url=FILE_FIXTURE_MANIFEST_URL):
"""Add file contents to Pulp.
Expand Down

0 comments on commit 4c48d6b

Please sign in to comment.