Skip to content

Commit

Permalink
Package: add endpoints to remove, add files to packages
Browse files Browse the repository at this point in the history
These support a new archivematica-devtools script to reindex files
from the ES index.
  • Loading branch information
Misty De Meo committed Mar 9, 2015
1 parent b1a62e8 commit edf16d3
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 3 deletions.
93 changes: 90 additions & 3 deletions storage_service/locations/api/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ def prepend_urls(self):
url(r"^(?P<resource_name>%s)/(?P<%s>\w[\w/-]*)/pointer_file%s$" % (self._meta.resource_name, self._meta.detail_uri_name, trailing_slash()), self.wrap_view('pointer_file_request'), name="pointer_file_request"),
url(r"^(?P<resource_name>%s)/(?P<%s>\w[\w/-]*)/check_fixity%s$" % (self._meta.resource_name, self._meta.detail_uri_name, trailing_slash()), self.wrap_view('check_fixity_request'), name="check_fixity_request"),
url(r"^(?P<resource_name>%s)/(?P<%s>\w[\w/-]*)/send_callback/post_store%s$" % (self._meta.resource_name, self._meta.detail_uri_name, trailing_slash()), self.wrap_view('aip_store_callback_request'), name="aip_store_callback_request"),
url(r"^(?P<resource_name>%s)/(?P<%s>\w[\w/-]*)/contents%s$" % (self._meta.resource_name, self._meta.detail_uri_name, trailing_slash()), self.wrap_view("package_contents"), name="package_contents"),
url(r"^(?P<resource_name>%s)/(?P<%s>\w[\w/-]*)/contents%s$" % (self._meta.resource_name, self._meta.detail_uri_name, trailing_slash()), self.wrap_view("manage_contents"), name="manage_contents"),
url(r"^(?P<resource_name>%s)/metadata%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view("file_data"), name="file_data"),

# FEDORA/SWORD2 endpoints
Expand Down Expand Up @@ -773,8 +773,95 @@ def _attempt_package_request_event(self, package, request_info, event_type, even

return (status_code, response)

@_custom_endpoint(expected_methods=['get'])
def package_contents(self, request, bundle, **kwargs):
@_custom_endpoint(expected_methods=['get', 'put', 'delete'])
def manage_contents(self, request, bundle, **kwargs):
if request.method == 'PUT':
return self._add_files_to_package(request, bundle, **kwargs)
elif request.method == 'DELETE':
return self._remove_files_from_package(request, bundle, **kwargs)
elif request.method == 'GET':
return self._package_contents(request, bundle, **kwargs)

def _remove_files_from_package(self, request, bundle, **kwargs):
"""
Removes all file records associated with this package.
"""

bundle.obj.file_set.all().delete()
return http.HttpNoContent()

def _add_files_to_package(self, request, bundle, **kwargs):
"""
Adds a set of files to a package.
The PUT body must be a list of zero or more JavaScript objects in the following format:
{
"relative_path": "string",
"fileuuid": "string",
"accessionid", "string",
"sipuuid": "string",
"origin": "string"
}
"""

try:
files_list = json.load(request)
except ValueError:
response = {
"success": False,
"error": "No JSON object could be decoded from POST body."
}
return http.HttpBadRequest(json.dumps(response),
content_type="application/json")

if not isinstance(files_list, list):
response = {
"success": False,
"error": "JSON request must contain a list of objects."
}
return http.HttpBadRequest(json.dumps(response),
content_type="application/json")

property_map = {
"relative_path": "name",
"fileuuid": "source_id",
"accessionid": "accessionid",
"sipuuid": "source_package",
"origin": "origin",
}

if len(files_list) == 0:
return http.HttpResponse()

created_files = []
for f in files_list:
kwargs = {
"package": bundle.obj
}
for source, dest in property_map.iteritems():
try:
kwargs[dest] = f[source]
except KeyError:
response = {
"success": False,
"error": "File object was missing key: " + source
}
return http.HttpBadRequest(json.dumps(response),
content_type="application_json")

created_files.append(File(**kwargs))

for f in created_files:
f.save()

response = {
"success": True,
"message": "{} files created in package {}".format(len(created_files), bundle.obj.uuid)
}
return http.HttpCreated(json.dumps(response),
content_type="application_json")

def _package_contents(self, request, bundle, **kwargs):
"""
Returns metadata about every file within a specified Storage Service
package, specified via Storage Service UUID.
Expand Down
49 changes: 49 additions & 0 deletions storage_service/locations/fixtures/base.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,54 @@
"accessionid": "",
"origin": "bd17e3cf-afb6-4067-b7d0-472482767ee2"
}
},
{
"pk": 2,
"model": "locations.package",
"fields": {
"uuid": "79245866-ca80-4f84-b904-a02b3e0ab621",
"description": "Package with no files",
"origin_pipeline": null,
"current_location": "4056b25d-6a85-4557-b9a5-9c85565fd892",
"current_path": "/dev/null/empty-transfer-79245866-ca80-4f84-b904-a02b3e0ab621",
"pointer_file_location": "",
"pointer_file_path": "",
"size": 0,
"package_type": "Transfer",
"status": "Uploaded",
"misc_attributes": ""
}
},
{
"pk": 3,
"model": "locations.package",
"fields": {
"uuid": "a59033c2-7fa7-41e2-9209-136f07174692",
"description": "Package with one file",
"origin_pipeline": null,
"current_location": "4056b25d-6a85-4557-b9a5-9c85565fd892",
"current_path": "/dev/null/transfer-with-one-file-a59033c2-7fa7-41e2-9209-136f07174692",
"pointer_file_location": "",
"pointer_file_path": "",
"size": 0,
"package_type": "Transfer",
"status": "Uploaded",
"misc_attributes": ""
}
},
{
"pk": 2,
"model": "locations.file",
"fields": {
"uuid": "bcd59769-0c6b-48cd-b54a-63092b9718fc",
"package": 3,
"name": "test_sip/objects/file.txt",
"source_id": "2b24a977-ad7a-4886-b17c-8b32ab4a7955",
"source_package": "a59033c2-7fa7-41e2-9209-136f07174692",
"checksum": "",
"stored": false,
"accessionid": "",
"origin": "91d13621-d2c1-4c70-a67e-77e96dced036"
}
}
]
65 changes: 65 additions & 0 deletions storage_service/locations/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,68 @@ def test_package_contents_returns_metadata(self):
assert body['success'] is True
assert len(body['files']) == 1
assert body['files'][0]['name'] == 'test_sip/objects/file.txt'

def test_adding_package_files_returns_400_with_empty_post_body(self):
response = self.client.put('/api/v2/file/e0a41934-c1d7-45ba-9a95-a7531c063ed1/contents/',
data="", content_type="application/json")
assert response.status_code == 400

def test_adding_package_files_returns_400_if_post_body_is_not_json(self):
response = self.client.put('/api/v2/file/e0a41934-c1d7-45ba-9a95-a7531c063ed1/contents/',
data="not json!",
content_type="application/json")
assert response.status_code == 400

def test_adding_package_files_returns_400_if_post_body_is_not_a_list(self):
response = self.client.put('/api/v2/file/e0a41934-c1d7-45ba-9a95-a7531c063ed1/contents/',
data="{}", content_type="application/json")
assert response.status_code == 400

def test_adding_package_files_returns_400_if_expected_fields_are_missing(self):
body = [{
"relative_path": "/dev/null"
}]
response = self.client.put('/api/v2/file/e0a41934-c1d7-45ba-9a95-a7531c063ed1/contents/',
data=json.dumps(body),
content_type="application/json")
assert response.status_code == 400

def test_adding_files_to_package_returns_200_for_empty_list(self):
response = self.client.put('/api/v2/file/79245866-ca80-4f84-b904-a02b3e0ab621/contents/',
data='[]', content_type="application/json")
assert response.status_code == 200

def test_adding_files_to_package(self):
p = models.Package.objects.get(uuid="79245866-ca80-4f84-b904-a02b3e0ab621")
assert p.file_set.count() == 0

body = [
{
"relative_path": "empty-transfer-79245866-ca80-4f84-b904-a02b3e0ab621/1.txt",
"fileuuid": "7bffcce7-63f5-4b2e-af57-d266bfa2e3eb",
"accessionid": "",
"sipuuid": "79245866-ca80-4f84-b904-a02b3e0ab621",
"origin": "36398145-6e49-4b5b-af02-209b127f2726",
},
{
"relative_path": "empty-transfer-79245866-ca80-4f84-b904-a02b3e0ab621/2.txt",
"fileuuid": "152be912-819f-49c4-968f-d5ce959c1cb1",
"accessionid": "",
"sipuuid": "79245866-ca80-4f84-b904-a02b3e0ab621",
"origin": "36398145-6e49-4b5b-af02-209b127f2726",
},
]

response = self.client.put('/api/v2/file/79245866-ca80-4f84-b904-a02b3e0ab621/contents/',
data=json.dumps(body),
content_type="application/json")
assert response.status_code == 201
assert p.file_set.count() == 2

def test_removing_file_from_package(self):
p = models.Package.objects.get(uuid="a59033c2-7fa7-41e2-9209-136f07174692")
assert p.file_set.count() == 1

response = self.client.delete('/api/v2/file/a59033c2-7fa7-41e2-9209-136f07174692/contents/')
assert response.status_code == 204
assert p.file_set.count() == 0

0 comments on commit edf16d3

Please sign in to comment.