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

Commit 4ac54e1

Browse files
author
David Read
committed
[api]: ckan#662 fix for resubmitting package from api.
1 parent 9e44b4d commit 4ac54e1

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

ckan/controllers/apiv1/package.py

+30
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import copy
2+
13
from ckan.controllers.rest import RestController
24
from ckan.lib.base import _, request, response
35
from ckan.lib.cache import ckan_cache
@@ -8,6 +10,11 @@
810

911
log = __import__("logging").getLogger(__name__)
1012

13+
readonly_keys = ('id', 'relationships', 'ratings_average',
14+
'ratings_count', 'ckan_url',
15+
'metadata_modified',
16+
'metadata_created')
17+
1118
class PackageController(RestController):
1219

1320
extensions = PluginImplementations(IPackageController)
@@ -59,6 +66,7 @@ def create(self):
5966
fs = self._get_standard_package_fieldset()
6067
try:
6168
request_data = self._get_request_data()
69+
request_data = self._strip_readonly_keys(request_data)
6270
request_fa_dict = ckan.forms.edit_package_dict(ckan.forms.get_package_dict(fs=fs), request_data)
6371
fs = fs.bind(model.Package, data=request_fa_dict, session=model.Session)
6472
log.debug('Created object %s' % str(fs.name.value))
@@ -124,6 +132,8 @@ def update(self, id):
124132
orig_entity_dict = ckan.forms.get_package_dict(pkg=entity, fs=fs)
125133
try:
126134
request_data = self._get_request_data()
135+
request_data = self._strip_readonly_keys(request_data,
136+
entity.as_dict())
127137
request_fa_dict = ckan.forms.edit_package_dict(orig_entity_dict, request_data, id=entity.id)
128138
fs = fs.bind(entity, data=request_fa_dict)
129139
validation = fs.validate()
@@ -179,3 +189,23 @@ def delete(self, id):
179189
log.exception(inst)
180190
raise
181191
return self._finish_ok()
192+
193+
def _strip_readonly_keys(self, request_dict, existing_pkg_dict=None):
194+
'''Removes keys that are readonly. If there is an existing package,
195+
the values of the keys are checked against to see if they have
196+
been inadvertantly edited - if so, raise an error.
197+
'''
198+
stripped_package_dict = copy.deepcopy(request_dict)
199+
for key in readonly_keys:
200+
if request_dict.has_key(key):
201+
if existing_pkg_dict:
202+
if request_dict[key] != existing_pkg_dict.get(key):
203+
raise ckan.forms.PackageDictFormatError(
204+
'Key %r is readonly - do not include in the '
205+
'package or leave it unchanged.')
206+
else:
207+
raise ckan.forms.PackageDictFormatError(
208+
'Key %r is readonly - do not include in the '
209+
'package.')
210+
del stripped_package_dict[key]# = request_dict[key]
211+
return stripped_package_dict

ckan/tests/functional/api/model/test_package.py

+34-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def test_register_post_ok(self):
8484
postparams = '%s=1' % self.dumps(self.package_fixture_data)
8585
res = self.app.post(offset, params=postparams, status=self.STATUS_409_CONFLICT,
8686
extra_environ=self.extra_environ)
87-
self.remove()
87+
self.remove()
8888

8989
def test_register_post_bad_request(self):
9090
test_params = {
@@ -101,6 +101,17 @@ def test_register_post_denied(self):
101101
postparams = '%s=1' % self.dumps(self.package_fixture_data)
102102
res = self.app.post(offset, params=postparams, status=self.STATUS_403_ACCESS_DENIED)
103103

104+
def test_register_post_readonly_fields(self):
105+
# (ticket 662) Post a package with readonly field such as 'id'
106+
offset = self.offset('/rest/package')
107+
data = {'name': u'test_readonly',
108+
'id': u'not allowed to be set',
109+
}
110+
postparams = '%s=1' % self.dumps(data)
111+
res = self.app.post(offset, params=postparams,
112+
status=self.STATUS_400_BAD_REQUEST,
113+
extra_environ=self.extra_environ)
114+
104115
def test_entity_get_ok(self):
105116
package_refs = [self.anna.name, self.anna.id]
106117
for ref in package_refs:
@@ -122,6 +133,28 @@ def test_entity_get_not_found(self):
122133
res = self.app.get(offset, status=self.STATUS_404_NOT_FOUND)
123134
self.remove()
124135

136+
def test_entity_get_then_post(self):
137+
# (ticket 662) Ensure an entity you 'get' from a register can be
138+
# returned by posting it back
139+
offset = self.package_offset(self.war.name)
140+
res = self.app.get(offset, status=self.STATUS_200_OK)
141+
data = self.loads(res.body)
142+
postparams = '%s=1' % self.dumps(data)
143+
res = self.app.post(offset, params=postparams,
144+
status=self.STATUS_200_OK,
145+
extra_environ=self.extra_environ)
146+
147+
def test_entity_post_changed_readonly(self):
148+
# (ticket 662) Edit a readonly field gives error
149+
offset = self.package_offset(self.war.name)
150+
res = self.app.get(offset, status=self.STATUS_200_OK)
151+
data = self.loads(res.body)
152+
data['id'] = 'illegally changed value'
153+
postparams = '%s=1' % self.dumps(data)
154+
res = self.app.post(offset, params=postparams,
155+
status=self.STATUS_400_BAD_REQUEST,
156+
extra_environ=self.extra_environ)
157+
125158
def test_entity_update_denied(self):
126159
offset = self.anna_offset()
127160
postparams = '%s=1' % self.dumps(self.package_fixture_data)

0 commit comments

Comments
 (0)