From 8df768d6a598ae54fe24c041f719020b9066c439 Mon Sep 17 00:00:00 2001 From: Denis Zgonjanin Date: Fri, 25 Apr 2014 11:54:48 -0400 Subject: [PATCH 01/12] package patch action, for issue #1416 --- ckan/logic/action/update.py | 27 ++++++++++++++-- ckan/tests/logic/test_action.py | 56 +++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index 45c4035a2c8..6954d8ed81a 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -347,7 +347,7 @@ def package_update(context, data_dict): You must be authorized to edit the dataset and the groups that it belongs to. - + It is recommended to call :py:func:`ckan.logic.action.get.package_show`, make the desired changes to the result, and then call ``package_update()`` with it. @@ -454,6 +454,29 @@ def package_update(context, data_dict): return output +def package_patch(context, data_dict): + '''Patch a dataset (package). + + The difference between the update and patch methods is that the patch will + perform an update of the provided parameters, while leaving all other + parameters unchanged, whereas the update methods deletes all parameters + not explicitly provided in the data_dict + + You must be authorized to edit the dataset and the groups that it belongs + to. + + ''' + + name = data_dict.get('name') + if name: + package_dict = _get_action('package_show')(context, {'name': name}) + else: + package_dict = _get_action('package_show')(context, {'id': data_dict['id']}) + + patched = dict(package_dict.items() + data_dict.items()) + return package_update(context, patched) + + def package_resource_reorder(context, data_dict): '''Reorder resources against datasets. If only partial resource ids are supplied then these are assumed to be first and the other resources will @@ -869,7 +892,7 @@ def task_status_update_many(context, data_dict): '''Update many task statuses at once. :param data: the task_status dictionaries to update, for the format of task - status dictionaries see + status dictionaries see :py:func:`~task_status_update` :type data: list of dictionaries diff --git a/ckan/tests/logic/test_action.py b/ckan/tests/logic/test_action.py index dc50df69a17..7057a66b807 100644 --- a/ckan/tests/logic/test_action.py +++ b/ckan/tests/logic/test_action.py @@ -220,6 +220,62 @@ def test_03_create_update_package(self): package_created.pop('metadata_modified') assert package_updated == package_created#, (pformat(json.loads(res.body)), pformat(package_created['result'])) + def test_03_create_patch_package(self): + + package = { + 'author': None, + 'author_email': None, + 'extras': [{'key': u'original media','value': u'"book"'}], + 'license_id': u'other-open', + 'maintainer': None, + 'maintainer_email': None, + 'name': u'annakareninanew2', + 'notes': u'Some test now', + 'resources': [{'alt_url': u'alt123', + 'description': u'Full text.', + 'extras': {u'alt_url': u'alt123', u'size': u'123'}, + 'format': u'plain text', + 'hash': u'abc123', + 'position': 0, + 'url': u'http://www.annakarenina.com/download/'}, + {'alt_url': u'alt345', + 'description': u'Index of the novel', + 'extras': {u'alt_url': u'alt345', u'size': u'345'}, + 'format': u'JSON', + 'hash': u'def456', + 'position': 1, + 'url': u'http://www.annakarenina.com/index.json'}], + 'tags': [{'name': u'russian'}, {'name': u'tolstoy'}], + 'title': u'A Novel By Tolstoy', + 'url': u'http://www.annakarenina.com', + 'version': u'0.7a' + } + + wee = json.dumps(package) + postparams = '%s=1' % json.dumps(package) + res = self.app.post('/api/action/package_create', params=postparams, + extra_environ={'Authorization': str(self.sysadmin_user.apikey)}) + package_created = json.loads(res.body)['result'] + print package_created + + postparams = '%s=1' % json.dumps({'id': package_created['id'], 'notes': 'Some new notes'}) + res = self.app.post('/api/action/package_patch', params=postparams, + extra_environ={'Authorization': str(self.sysadmin_user.apikey)}) + + package_patched = json.loads(res.body)['result'] + package_patched.pop('revision_id') + package_patched.pop('revision_timestamp') + package_patched.pop('metadata_created') + package_patched.pop('metadata_modified') + + package_created.pop('revision_id') + package_created.pop('revision_timestamp') + package_created.pop('metadata_created') + package_created.pop('metadata_modified') + package_created['notes'] = 'Some new notes' + + assert package_patched == package_created#, (pformat(json.loads(res.body)), pformat(package_created['result'])) + def test_03_create_private_package(self): # Make an organization, because private datasets must belong to one. From 775a6a6efdd3f632f0ca34db48f8436e0178fdb7 Mon Sep 17 00:00:00 2001 From: Denis Zgonjanin Date: Fri, 25 Apr 2014 12:38:14 -0400 Subject: [PATCH 02/12] [#1416] improve patch method --- ckan/logic/action/update.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index 6954d8ed81a..c995f96bb85 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -467,11 +467,8 @@ def package_patch(context, data_dict): ''' - name = data_dict.get('name') - if name: - package_dict = _get_action('package_show')(context, {'name': name}) - else: - package_dict = _get_action('package_show')(context, {'id': data_dict['id']}) + name_or_id = data_dict.get("name") or _get_or_bust(data_dict, "id") + package_dict = _get_action('package_show')(context, {'id': name_or_id}) patched = dict(package_dict.items() + data_dict.items()) return package_update(context, patched) From ed4067a67e4e0fa6574f048a06bfd2443fac9551 Mon Sep 17 00:00:00 2001 From: Denis Zgonjanin Date: Fri, 25 Apr 2014 15:34:29 -0400 Subject: [PATCH 03/12] [#1416] add auth function to package_patch action --- ckan/logic/action/update.py | 2 ++ ckan/logic/auth/update.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index c995f96bb85..8185ddbc94e 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -467,6 +467,8 @@ def package_patch(context, data_dict): ''' + _check_access('package_patch', context, data_dict) + name_or_id = data_dict.get("name") or _get_or_bust(data_dict, "id") package_dict = _get_action('package_show')(context, {'id': name_or_id}) diff --git a/ckan/logic/auth/update.py b/ckan/logic/auth/update.py index f2053c5871f..df50a117301 100644 --- a/ckan/logic/auth/update.py +++ b/ckan/logic/auth/update.py @@ -48,6 +48,10 @@ def package_update(context, data_dict): return {'success': True} +@logic.auth_allow_anonymous_access +def package_patch(context, data_dict): + return package_update(context, data_dict) + def package_resource_reorder(context, data_dict): ## the action function runs package update so no need to run it twice return {'success': True} From cc720cf0476c97bb95831b66f5d263c406316e4f Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Mon, 3 Nov 2014 13:25:32 -0500 Subject: [PATCH 04/12] [#2003] separate context for package_show --- ckan/logic/action/update.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index e63326dc061..2db917402a4 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -471,7 +471,14 @@ def package_patch(context, data_dict): _check_access('package_patch', context, data_dict) name_or_id = data_dict.get("name") or _get_or_bust(data_dict, "id") - package_dict = _get_action('package_show')(context, {'id': name_or_id}) + show_context = { + 'model': context['model'], + 'session': context['session'], + 'user': context['user'], + 'auth_user_obj': context['auth_user_obj'], + } + + package_dict = _get_action('package_show')(show_context, {'id': name_or_id}) patched = dict(package_dict.items() + data_dict.items()) return package_update(context, patched) From 20c44b61ef4399a9e83d63d4c69d2518a40d42da Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Mon, 3 Nov 2014 14:21:21 -0500 Subject: [PATCH 05/12] [#2003] start separate file for patch actions --- ckan/logic/__init__.py | 2 +- ckan/logic/action/patch.py | 32 ++++++++++++++++++++++++++++++++ ckan/logic/action/update.py | 29 ----------------------------- ckan/logic/auth/patch.py | 4 ++++ ckan/logic/auth/update.py | 4 ---- ckan/new_authz.py | 2 +- 6 files changed, 38 insertions(+), 35 deletions(-) create mode 100644 ckan/logic/action/patch.py create mode 100644 ckan/logic/auth/patch.py diff --git a/ckan/logic/__init__.py b/ckan/logic/__init__.py index c759b3e0081..ec07620ef6b 100644 --- a/ckan/logic/__init__.py +++ b/ckan/logic/__init__.py @@ -360,7 +360,7 @@ def get_action(action): # Rather than writing them out in full will use __import__ # to load anything from ckan.logic.action that looks like it might # be an action - for action_module_name in ['get', 'create', 'update', 'delete']: + for action_module_name in ['get', 'create', 'update', 'delete', 'patch']: module_path = 'ckan.logic.action.' + action_module_name module = __import__(module_path) for part in module_path.split('.')[1:]: diff --git a/ckan/logic/action/patch.py b/ckan/logic/action/patch.py new file mode 100644 index 00000000000..5796b0be176 --- /dev/null +++ b/ckan/logic/action/patch.py @@ -0,0 +1,32 @@ +'''API functions for partial updates of existing data in CKAN''' + +import ckan.logic.action.update as _update +from ckan.logic import get_action as _get_action + +def package_patch(context, data_dict): + '''Patch a dataset (package). + + The difference between the update and patch methods is that the patch will + perform an update of the provided parameters, while leaving all other + parameters unchanged, whereas the update methods deletes all parameters + not explicitly provided in the data_dict + + You must be authorized to edit the dataset and the groups that it belongs + to. + + ''' + + _check_access('package_patch', context, data_dict) + + name_or_id = data_dict.get("name") or _get_or_bust(data_dict, "id") + show_context = { + 'model': context['model'], + 'session': context['session'], + 'user': context['user'], + 'auth_user_obj': context['auth_user_obj'], + } + + package_dict = _get_action('package_show')(show_context, {'id': name_or_id}) + + patched = dict(package_dict.items() + data_dict.items()) + return _update.package_update(context, patched) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index 2db917402a4..e257b603a33 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -455,35 +455,6 @@ def package_update(context, data_dict): return output -def package_patch(context, data_dict): - '''Patch a dataset (package). - - The difference between the update and patch methods is that the patch will - perform an update of the provided parameters, while leaving all other - parameters unchanged, whereas the update methods deletes all parameters - not explicitly provided in the data_dict - - You must be authorized to edit the dataset and the groups that it belongs - to. - - ''' - - _check_access('package_patch', context, data_dict) - - name_or_id = data_dict.get("name") or _get_or_bust(data_dict, "id") - show_context = { - 'model': context['model'], - 'session': context['session'], - 'user': context['user'], - 'auth_user_obj': context['auth_user_obj'], - } - - package_dict = _get_action('package_show')(show_context, {'id': name_or_id}) - - patched = dict(package_dict.items() + data_dict.items()) - return package_update(context, patched) - - def package_resource_reorder(context, data_dict): '''Reorder resources against datasets. If only partial resource ids are supplied then these are assumed to be first and the other resources will diff --git a/ckan/logic/auth/patch.py b/ckan/logic/auth/patch.py new file mode 100644 index 00000000000..579c52ab3b8 --- /dev/null +++ b/ckan/logic/auth/patch.py @@ -0,0 +1,4 @@ +from ckan import logic +import ckan.logic.auth.update as _update + +package_patch = _update.package_update diff --git a/ckan/logic/auth/update.py b/ckan/logic/auth/update.py index 9ed8e03dc01..59124718a56 100644 --- a/ckan/logic/auth/update.py +++ b/ckan/logic/auth/update.py @@ -48,10 +48,6 @@ def package_update(context, data_dict): return {'success': True} -@logic.auth_allow_anonymous_access -def package_patch(context, data_dict): - return package_update(context, data_dict) - def package_resource_reorder(context, data_dict): ## the action function runs package update so no need to run it twice return {'success': True} diff --git a/ckan/new_authz.py b/ckan/new_authz.py index 7d4cdc3716d..2c98d816762 100644 --- a/ckan/new_authz.py +++ b/ckan/new_authz.py @@ -44,7 +44,7 @@ def _build(self): module_root = 'ckan.logic.auth' - for auth_module_name in ['get', 'create', 'update', 'delete']: + for auth_module_name in ['get', 'create', 'update', 'delete', 'patch']: module_path = '%s.%s' % (module_root, auth_module_name,) try: module = __import__(module_path) From 616eaca6780402789f65806190b8bc07338b871d Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Mon, 3 Nov 2014 14:53:19 -0500 Subject: [PATCH 06/12] [#2003] smaller, simpler, new_test for package_patch --- ckan/new_tests/logic/action/test_patch.py | 32 +++++++++++++ ckan/tests/logic/test_action.py | 56 ----------------------- 2 files changed, 32 insertions(+), 56 deletions(-) create mode 100644 ckan/new_tests/logic/action/test_patch.py diff --git a/ckan/new_tests/logic/action/test_patch.py b/ckan/new_tests/logic/action/test_patch.py new file mode 100644 index 00000000000..f0427454b0f --- /dev/null +++ b/ckan/new_tests/logic/action/test_patch.py @@ -0,0 +1,32 @@ +'''Unit tests for ckan/logic/action/update.py.''' +import datetime + +from nose.tools import assert_equals, assert_raises +import mock +import pylons.config as config + +#import ckan.logic as logic +from ckan.new_tests import helpers +#import ckan.new_tests.factories as factories + +class TestPatch(helpers.FunctionalTestBase): + + def test_package_patch_updating_single_field(self): + user = factories.User() + dataset = factories.Dataset( + name='annakarenina', + notes='some test now', + user=user) + + dataset = helpers.call_action( + 'package_patch', + id=dataset['id'], + name='somethingnew', + ) + + assert_equals(dataset['name'], 'somethingnew') + assert_equals(dataset['notes'], 'some test now') + + assert_equals( + helpers.call_action('package_show', id='somethingnew')['notes'], + 'some test now') diff --git a/ckan/tests/logic/test_action.py b/ckan/tests/logic/test_action.py index 277fc3a84ce..9b3d4476f16 100644 --- a/ckan/tests/logic/test_action.py +++ b/ckan/tests/logic/test_action.py @@ -220,62 +220,6 @@ def test_03_create_update_package(self): package_created.pop('metadata_modified') assert package_updated == package_created#, (pformat(json.loads(res.body)), pformat(package_created['result'])) - def test_03_create_patch_package(self): - - package = { - 'author': None, - 'author_email': None, - 'extras': [{'key': u'original media','value': u'"book"'}], - 'license_id': u'other-open', - 'maintainer': None, - 'maintainer_email': None, - 'name': u'annakareninanew2', - 'notes': u'Some test now', - 'resources': [{'alt_url': u'alt123', - 'description': u'Full text.', - 'extras': {u'alt_url': u'alt123', u'size': u'123'}, - 'format': u'plain text', - 'hash': u'abc123', - 'position': 0, - 'url': u'http://www.annakarenina.com/download/'}, - {'alt_url': u'alt345', - 'description': u'Index of the novel', - 'extras': {u'alt_url': u'alt345', u'size': u'345'}, - 'format': u'JSON', - 'hash': u'def456', - 'position': 1, - 'url': u'http://www.annakarenina.com/index.json'}], - 'tags': [{'name': u'russian'}, {'name': u'tolstoy'}], - 'title': u'A Novel By Tolstoy', - 'url': u'http://www.annakarenina.com', - 'version': u'0.7a' - } - - wee = json.dumps(package) - postparams = '%s=1' % json.dumps(package) - res = self.app.post('/api/action/package_create', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}) - package_created = json.loads(res.body)['result'] - print package_created - - postparams = '%s=1' % json.dumps({'id': package_created['id'], 'notes': 'Some new notes'}) - res = self.app.post('/api/action/package_patch', params=postparams, - extra_environ={'Authorization': str(self.sysadmin_user.apikey)}) - - package_patched = json.loads(res.body)['result'] - package_patched.pop('revision_id') - package_patched.pop('revision_timestamp') - package_patched.pop('metadata_created') - package_patched.pop('metadata_modified') - - package_created.pop('revision_id') - package_created.pop('revision_timestamp') - package_created.pop('metadata_created') - package_created.pop('metadata_modified') - package_created['notes'] = 'Some new notes' - - assert package_patched == package_created#, (pformat(json.loads(res.body)), pformat(package_created['result'])) - def test_03_create_private_package(self): # Make an organization, because private datasets must belong to one. From 8459491800ad4f77521f7a7a0621acc840fda088 Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Mon, 3 Nov 2014 15:15:46 -0500 Subject: [PATCH 07/12] [#2003] fix missing symbol --- ckan/logic/action/patch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckan/logic/action/patch.py b/ckan/logic/action/patch.py index 5796b0be176..1c3644370a2 100644 --- a/ckan/logic/action/patch.py +++ b/ckan/logic/action/patch.py @@ -1,7 +1,7 @@ '''API functions for partial updates of existing data in CKAN''' import ckan.logic.action.update as _update -from ckan.logic import get_action as _get_action +from ckan.logic import get_action as _get_action, check_access as _check_access def package_patch(context, data_dict): '''Patch a dataset (package). From d8e9cb2889544fa2e07cc88570377da3e6dac424 Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Mon, 3 Nov 2014 15:27:05 -0500 Subject: [PATCH 08/12] [#2003] pep8 --- ckan/logic/action/patch.py | 5 ++++- ckan/new_tests/logic/action/test_patch.py | 10 ++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ckan/logic/action/patch.py b/ckan/logic/action/patch.py index 1c3644370a2..b0ed4f6819e 100644 --- a/ckan/logic/action/patch.py +++ b/ckan/logic/action/patch.py @@ -3,6 +3,7 @@ import ckan.logic.action.update as _update from ckan.logic import get_action as _get_action, check_access as _check_access + def package_patch(context, data_dict): '''Patch a dataset (package). @@ -26,7 +27,9 @@ def package_patch(context, data_dict): 'auth_user_obj': context['auth_user_obj'], } - package_dict = _get_action('package_show')(show_context, {'id': name_or_id}) + package_dict = _get_action('package_show')( + show_context, + {'id': name_or_id}) patched = dict(package_dict.items() + data_dict.items()) return _update.package_update(context, patched) diff --git a/ckan/new_tests/logic/action/test_patch.py b/ckan/new_tests/logic/action/test_patch.py index f0427454b0f..8c11c4e043f 100644 --- a/ckan/new_tests/logic/action/test_patch.py +++ b/ckan/new_tests/logic/action/test_patch.py @@ -1,13 +1,12 @@ -'''Unit tests for ckan/logic/action/update.py.''' +'''Unit tests for ckan/logic/action/patch.py.''' import datetime from nose.tools import assert_equals, assert_raises import mock import pylons.config as config -#import ckan.logic as logic -from ckan.new_tests import helpers -#import ckan.new_tests.factories as factories +from ckan.new_tests import helpers, factories + class TestPatch(helpers.FunctionalTestBase): @@ -21,8 +20,7 @@ def test_package_patch_updating_single_field(self): dataset = helpers.call_action( 'package_patch', id=dataset['id'], - name='somethingnew', - ) + name='somethingnew') assert_equals(dataset['name'], 'somethingnew') assert_equals(dataset['notes'], 'some test now') From 7e46789e91d115ea303d7cbda888c9aba31db0ec Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Mon, 3 Nov 2014 16:30:14 -0500 Subject: [PATCH 09/12] [#2003] test package_patch: get by original id --- ckan/logic/action/patch.py | 9 ++++++--- ckan/new_tests/logic/action/test_patch.py | 7 ++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ckan/logic/action/patch.py b/ckan/logic/action/patch.py index b0ed4f6819e..6d8ebfb5ed4 100644 --- a/ckan/logic/action/patch.py +++ b/ckan/logic/action/patch.py @@ -1,7 +1,11 @@ '''API functions for partial updates of existing data in CKAN''' import ckan.logic.action.update as _update -from ckan.logic import get_action as _get_action, check_access as _check_access +from ckan.logic import ( + get_action as _get_action, + check_access as _check_access, + get_or_bust as _get_or_bust, +) def package_patch(context, data_dict): @@ -19,7 +23,6 @@ def package_patch(context, data_dict): _check_access('package_patch', context, data_dict) - name_or_id = data_dict.get("name") or _get_or_bust(data_dict, "id") show_context = { 'model': context['model'], 'session': context['session'], @@ -29,7 +32,7 @@ def package_patch(context, data_dict): package_dict = _get_action('package_show')( show_context, - {'id': name_or_id}) + {'id': _get_or_bust(data_dict, 'id')}) patched = dict(package_dict.items() + data_dict.items()) return _update.package_update(context, patched) diff --git a/ckan/new_tests/logic/action/test_patch.py b/ckan/new_tests/logic/action/test_patch.py index 8c11c4e043f..48a9970e39f 100644 --- a/ckan/new_tests/logic/action/test_patch.py +++ b/ckan/new_tests/logic/action/test_patch.py @@ -25,6 +25,7 @@ def test_package_patch_updating_single_field(self): assert_equals(dataset['name'], 'somethingnew') assert_equals(dataset['notes'], 'some test now') - assert_equals( - helpers.call_action('package_show', id='somethingnew')['notes'], - 'some test now') + dataset2 = helpers.call_action('package_show', id=dataset['id']) + + assert_equals(dataset2['name'], 'somethingnew') + assert_equals(dataset2['notes'], 'some test now') From bd13557c08f81a7976f8ffb8d6df9f7684696487 Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Thu, 13 Nov 2014 15:20:37 -0500 Subject: [PATCH 10/12] [#2003] resource_patch --- ckan/logic/action/patch.py | 41 +++++++++++++++++++++-- ckan/logic/auth/patch.py | 2 ++ ckan/new_tests/logic/action/test_patch.py | 22 ++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/ckan/logic/action/patch.py b/ckan/logic/action/patch.py index 6d8ebfb5ed4..92251044213 100644 --- a/ckan/logic/action/patch.py +++ b/ckan/logic/action/patch.py @@ -11,6 +11,9 @@ def package_patch(context, data_dict): '''Patch a dataset (package). + :param id: the id or name of the dataset + :type id: string + The difference between the update and patch methods is that the patch will perform an update of the provided parameters, while leaving all other parameters unchanged, whereas the update methods deletes all parameters @@ -18,9 +21,7 @@ def package_patch(context, data_dict): You must be authorized to edit the dataset and the groups that it belongs to. - ''' - _check_access('package_patch', context, data_dict) show_context = { @@ -34,5 +35,39 @@ def package_patch(context, data_dict): show_context, {'id': _get_or_bust(data_dict, 'id')}) - patched = dict(package_dict.items() + data_dict.items()) + patched = dict(package_dict) + patched.update(data_dict) + patched['id'] = package_dict['id'] return _update.package_update(context, patched) + + +def resource_patch(context, data_dict): + '''Patch a dataset (package). + + :param id: the id of the resource + :type id: string + + The difference between the update and patch methods is that the patch will + perform an update of the provided parameters, while leaving all other + parameters unchanged, whereas the update methods deletes all parameters + not explicitly provided in the data_dict + + You must be authorized to edit the dataset and the groups that it belongs + to. + ''' + _check_access('resource_patch', context, data_dict) + + show_context = { + 'model': context['model'], + 'session': context['session'], + 'user': context['user'], + 'auth_user_obj': context['auth_user_obj'], + } + + resource_dict = _get_action('resource_show')( + show_context, + {'id': _get_or_bust(data_dict, 'id')}) + + patched = dict(resource_dict) + patched.update(data_dict) + return _update.resource_update(context, patched) diff --git a/ckan/logic/auth/patch.py b/ckan/logic/auth/patch.py index 579c52ab3b8..d0d01f758ef 100644 --- a/ckan/logic/auth/patch.py +++ b/ckan/logic/auth/patch.py @@ -2,3 +2,5 @@ import ckan.logic.auth.update as _update package_patch = _update.package_update + +resource_patch = _update.resource_update diff --git a/ckan/new_tests/logic/action/test_patch.py b/ckan/new_tests/logic/action/test_patch.py index 48a9970e39f..8d257e0c62a 100644 --- a/ckan/new_tests/logic/action/test_patch.py +++ b/ckan/new_tests/logic/action/test_patch.py @@ -29,3 +29,25 @@ def test_package_patch_updating_single_field(self): assert_equals(dataset2['name'], 'somethingnew') assert_equals(dataset2['notes'], 'some test now') + + def test_resource_patch_updating_single_field(self): + user = factories.User() + dataset = factories.Dataset( + name='annakarenina', + notes='some test now', + user=user, + resources=[{'url': 'http://example.com/resource'}]) + + resource = helpers.call_action( + 'resource_patch', + id=dataset['resources'][0]['id'], + name='somethingnew') + + assert_equals(resource['name'], 'somethingnew') + assert_equals(resource['url'], 'http://example.com/resource') + + dataset2 = helpers.call_action('package_show', id=dataset['id']) + + resource2 = dataset2['resources'][0] + assert_equals(resource2['name'], 'somethingnew') + assert_equals(resource2['url'], 'http://example.com/resource') From 45ef9eede66c17ff409dcadddc3c2003b772d7f0 Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Thu, 13 Nov 2014 16:01:21 -0500 Subject: [PATCH 11/12] [#2003] group_patch and organization_patch --- ckan/logic/action/patch.py | 65 +++++++++++++++++++++-- ckan/logic/auth/patch.py | 4 ++ ckan/new_tests/logic/action/test_patch.py | 44 +++++++++++++++ 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/ckan/logic/action/patch.py b/ckan/logic/action/patch.py index 92251044213..0137a669ce1 100644 --- a/ckan/logic/action/patch.py +++ b/ckan/logic/action/patch.py @@ -42,7 +42,7 @@ def package_patch(context, data_dict): def resource_patch(context, data_dict): - '''Patch a dataset (package). + '''Patch a resource :param id: the id of the resource :type id: string @@ -51,9 +51,6 @@ def resource_patch(context, data_dict): perform an update of the provided parameters, while leaving all other parameters unchanged, whereas the update methods deletes all parameters not explicitly provided in the data_dict - - You must be authorized to edit the dataset and the groups that it belongs - to. ''' _check_access('resource_patch', context, data_dict) @@ -71,3 +68,63 @@ def resource_patch(context, data_dict): patched = dict(resource_dict) patched.update(data_dict) return _update.resource_update(context, patched) + + +def group_patch(context, data_dict): + '''Patch a group + + :param id: the id or name of the group + :type id: string + + The difference between the update and patch methods is that the patch will + perform an update of the provided parameters, while leaving all other + parameters unchanged, whereas the update methods deletes all parameters + not explicitly provided in the data_dict + ''' + _check_access('group_patch', context, data_dict) + + show_context = { + 'model': context['model'], + 'session': context['session'], + 'user': context['user'], + 'auth_user_obj': context['auth_user_obj'], + } + + group_dict = _get_action('group_show')( + show_context, + {'id': _get_or_bust(data_dict, 'id')}) + + patched = dict(group_dict) + patched.pop('display_name', None) + patched.update(data_dict) + return _update.group_update(context, patched) + + +def organization_patch(context, data_dict): + '''Patch an organization + + :param id: the id or name of the organization + :type id: string + + The difference between the update and patch methods is that the patch will + perform an update of the provided parameters, while leaving all other + parameters unchanged, whereas the update methods deletes all parameters + not explicitly provided in the data_dict + ''' + _check_access('organization_patch', context, data_dict) + + show_context = { + 'model': context['model'], + 'session': context['session'], + 'user': context['user'], + 'auth_user_obj': context['auth_user_obj'], + } + + organization_dict = _get_action('organization_show')( + show_context, + {'id': _get_or_bust(data_dict, 'id')}) + + patched = dict(organization_dict) + patched.pop('display_name', None) + patched.update(data_dict) + return _update.organization_update(context, patched) diff --git a/ckan/logic/auth/patch.py b/ckan/logic/auth/patch.py index d0d01f758ef..75b06cd8d44 100644 --- a/ckan/logic/auth/patch.py +++ b/ckan/logic/auth/patch.py @@ -4,3 +4,7 @@ package_patch = _update.package_update resource_patch = _update.resource_update + +group_patch = _update.group_update + +organization_patch = _update.organization_update diff --git a/ckan/new_tests/logic/action/test_patch.py b/ckan/new_tests/logic/action/test_patch.py index 8d257e0c62a..8d258918ac6 100644 --- a/ckan/new_tests/logic/action/test_patch.py +++ b/ckan/new_tests/logic/action/test_patch.py @@ -51,3 +51,47 @@ def test_resource_patch_updating_single_field(self): resource2 = dataset2['resources'][0] assert_equals(resource2['name'], 'somethingnew') assert_equals(resource2['url'], 'http://example.com/resource') + + def test_group_patch_updating_single_field(self): + user = factories.User() + group = factories.Group( + name='economy', + description='some test now', + user=user) + + group = helpers.call_action( + 'group_patch', + id=group['id'], + description='somethingnew', + context={'user': user['name']}) + + assert_equals(group['name'], 'economy') + assert_equals(group['description'], 'somethingnew') + + group2 = helpers.call_action('group_show', id=group['id']) + + assert_equals(group2['name'], 'economy') + assert_equals(group2['description'], 'somethingnew') + + def test_organization_patch_updating_single_field(self): + user = factories.User() + organization = factories.Organization( + name='economy', + description='some test now', + user=user) + + organization = helpers.call_action( + 'organization_patch', + id=organization['id'], + description='somethingnew', + context={'user': user['name']}) + + assert_equals(organization['name'], 'economy') + assert_equals(organization['description'], 'somethingnew') + + organization2 = helpers.call_action( + 'organization_show', + id=organization['id']) + + assert_equals(organization2['name'], 'economy') + assert_equals(organization2['description'], 'somethingnew') From 0ca9f887145c16afc518362bb2430a4baa172ded Mon Sep 17 00:00:00 2001 From: Ian Ward Date: Thu, 13 Nov 2014 16:14:39 -0500 Subject: [PATCH 12/12] [#2003] reference docs --- doc/api/index.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/api/index.rst b/doc/api/index.rst index c38d362e758..e08fc77ece2 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -381,6 +381,14 @@ ckan.logic.action.update .. automodule:: ckan.logic.action.update :members: +ckan.logic.action.patch +======================= + +.. versionadded:: 2.3 + +.. automodule:: ckan.logic.action.patch + :members: + ckan.logic.action.delete ========================