diff --git a/ckanext/privatedatasets/actions.py b/ckanext/privatedatasets/actions.py
index 40582bb..1fdf8bf 100644
--- a/ckanext/privatedatasets/actions.py
+++ b/ckanext/privatedatasets/actions.py
@@ -19,6 +19,7 @@
import ckan.plugins as plugins
import ckanext.privatedatasets.constants as constants
+import db
import importlib
import logging
import pylons.config as config
@@ -29,6 +30,28 @@ PARSER_CONFIG_PROP = 'ckan.privatedatasets.parser'
def package_acquired(context, request_data):
+ '''
+ API action to be called every time a user acquires a dataset in an external service.
+
+ This API should be called to add the user to the list of allowed users.
+
+ Since each service can provide a different way of pushing the data, the received
+ data will be forwarded to the parser set in the preferences. This parser should
+ return a dict similar to the following one:
+ {'errors': ["...", "...", ...]
+ 'users_datasets': [{'user': 'user_name', 'datasets': ['ds1', 'ds2', ...]}, ...]}
+ 1) 'errors' contains the list of errors. It should be empty if no errors arised
+ while the notification is parsed
+ 2) 'users_datasets' is the lists of datasets available for each user (each element
+ of this list is a dictionary with two fields: user and datasets).
+
+ :parameter request_data: Depends on the parser
+ :type request_data: dict
+
+ :return: A list of warnings or None if the list of warnings is empty
+ :rtype: dict
+
+ '''
log.info('Notification received: %s' % request_data)
@@ -107,3 +130,59 @@ def package_acquired(context, request_data):
# Return warnings that inform about non-existing datasets
if len(warns) > 0:
return {'warns': warns}
+
+def acquisitions_list(context, data_dict):
+ '''
+ API to retrieve the list of datasets that have been acquired by a certain user
+
+ :parameter user: The user whose acquired dataset you want to retrieve. This parameter
+ is optional. If you don't include this identifier, the system will use the one
+ of the user that is performing the request
+ :type user: string
+
+ :return: The list of datarequest that has been acquired by the specified user
+ :rtype: list
+ '''
+
+ if data_dict is None:
+ data_dict = {}
+
+ if 'user' not in data_dict and 'user' in context:
+ data_dict['user'] = context['user']
+
+ plugins.toolkit.check_access(constants.ACQUISITIONS_LIST, context.copy(), data_dict)
+
+ # Init db
+ db.init_db(context['model'])
+
+ # Init the result array
+ result = []
+
+ # Check that the user exists
+ try:
+ plugins.toolkit.get_validator('user_name_exists')(data_dict['user'], context.copy())
+ except Exception:
+ raise plugins.toolkit.ValidationError('User %s does not exist' % data_dict['user'])
+
+ # Get the datasets acquired by the user
+ query = db.AllowedUser.get(user_name=data_dict['user'])
+
+ # Get the datasets
+ for dataset in query:
+ try:
+ dataset_show_func = 'dataset_show'
+ func_data_dict = {'id': dataset.package_id}
+ internal_context = context.copy()
+
+ # Check that the the dataset can be accessed and get its data
+ # FIX: If the check_access function is not called, an exception is risen.
+ plugins.toolkit.check_access(dataset_show_func, internal_context, func_data_dict)
+ dataset_dict = plugins.toolkit.get_action(dataset_show_func)(internal_context, func_data_dict)
+
+ # Only packages with state == 'active' can be shown
+ if dataset_dict.get('state', None) == 'active':
+ result.append(dataset_dict)
+ except Exception:
+ pass
+
+ return result
diff --git a/ckanext/privatedatasets/auth.py b/ckanext/privatedatasets/auth.py
index c1fb0e4..186221e 100644
--- a/ckanext/privatedatasets/auth.py
+++ b/ckanext/privatedatasets/auth.py
@@ -133,3 +133,7 @@ def resource_show(context, data_dict):
def package_acquired(context, data_dict):
# TODO: Improve security
return {'success': True}
+
+def acquisitions_list(context, data_dict):
+ # Users can get only their acquisitions list
+ return {'success': context['user'] == data_dict['user']}
diff --git a/ckanext/privatedatasets/constants.py b/ckanext/privatedatasets/constants.py
index bf47f81..8f00449 100644
--- a/ckanext/privatedatasets/constants.py
+++ b/ckanext/privatedatasets/constants.py
@@ -18,6 +18,7 @@
# along with CKAN Private Dataset Extension. If not, see .
ALLOWED_USERS = 'allowed_users'
+ACQUISITIONS_LIST = 'acquisitions_list'
ALLOWED_USERS_STR = 'allowed_users_str'
SEARCHABLE = 'searchable'
ACQUIRE_URL = 'acquire_url'
diff --git a/ckanext/privatedatasets/controllers/ui_controller.py b/ckanext/privatedatasets/controllers/ui_controller.py
index 336ea07..573ae25 100644
--- a/ckanext/privatedatasets/controllers/ui_controller.py
+++ b/ckanext/privatedatasets/controllers/ui_controller.py
@@ -17,10 +17,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with CKAN Private Dataset Extension. If not, see .
+import ckanext.privatedatasets.constants as constants
import ckan.lib.base as base
import ckan.model as model
import ckan.plugins as plugins
-import ckanext.privatedatasets.db as db
import logging
from ckan.common import _
@@ -32,8 +32,6 @@ class AcquiredDatasetsControllerUI(base.BaseController):
def user_acquired_datasets(self):
- db.init_db(model)
-
c = plugins.toolkit.c
context = {
'model': model,
@@ -44,23 +42,10 @@ class AcquiredDatasetsControllerUI(base.BaseController):
# Get user information
try:
c.user_dict = plugins.toolkit.get_action('user_show')(context.copy(), {'user_obj': c.userobj})
- c.user_dict['acquired_datasets'] = []
+ c.user_dict['acquired_datasets'] = plugins.toolkit.get_action(constants.ACQUISITIONS_LIST)(context.copy(), None)
except plugins.toolkit.ObjectNotFound:
plugins.toolkit.abort(404, _('User not found'))
except plugins.toolkit.NotAuthorized:
plugins.toolkit.abort(401, _('Not authorized to see this page'))
- # Get the datasets acquired by the user
- query = db.AllowedUser.get(user_name=context['user'])
-
- # Get the datasets
- for dataset in query:
- try:
- dataset_dict = plugins.toolkit.get_action('package_show')(context.copy(), {'id': dataset.package_id})
- # Only packages with state == 'active' can be shown
- if dataset_dict.get('state', None) == 'active':
- c.user_dict['acquired_datasets'].append(dataset_dict)
- except Exception:
- continue
-
return plugins.toolkit.render('user/dashboard_acquired.html')
diff --git a/ckanext/privatedatasets/plugin.py b/ckanext/privatedatasets/plugin.py
index 714a2dc..3bd8952 100644
--- a/ckanext/privatedatasets/plugin.py
+++ b/ckanext/privatedatasets/plugin.py
@@ -107,7 +107,8 @@ class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm):
return {'package_show': auth.package_show,
'package_update': auth.package_update,
'resource_show': auth.resource_show,
- constants.PACKAGE_ACQUIRED: auth.package_acquired}
+ constants.PACKAGE_ACQUIRED: auth.package_acquired,
+ constants.ACQUISITIONS_LIST: auth.acquisitions_list}
######################################################################
############################ ICONFIGURER #############################
@@ -138,7 +139,10 @@ class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm):
######################################################################
def get_actions(self):
- return {constants.PACKAGE_ACQUIRED: actions.package_acquired}
+ return {
+ constants.PACKAGE_ACQUIRED: actions.package_acquired,
+ constants.ACQUISITIONS_LIST: actions.acquisitions_list
+ }
######################################################################
######################### IPACKAGECONTROLLER #########################
diff --git a/ckanext/privatedatasets/tests/test_actions.py b/ckanext/privatedatasets/tests/test_actions.py
index f7f4523..bfb36cc 100644
--- a/ckanext/privatedatasets/tests/test_actions.py
+++ b/ckanext/privatedatasets/tests/test_actions.py
@@ -43,11 +43,15 @@ class ActionsTest(unittest.TestCase):
self._plugins = actions.plugins
actions.plugins = MagicMock()
+ self._db = actions.db
+ actions.db = MagicMock()
+
def tearDown(self):
# Unmock
actions.config = self._config
actions.importlib = self._importlib
actions.plugins = self._plugins
+ actions.db = self._db
@parameterized.expand([
('', None, False, False, '%s not configured' % PARSER_CONFIG_PROP),
@@ -201,3 +205,82 @@ class ActionsTest(unittest.TestCase):
context_update['ignore_auth'] = True
package_update.assert_any_call(context_update, {'id': dataset_id, 'allowed_users': expected_allowed_users, 'private': True})
+
+
+ @parameterized.expand([
+ (None, {},),
+ ({}, {2: actions.plugins.toolkit.ObjectNotFound},),
+ ({'user': 'fiware'}, {1: actions.plugins.toolkit.NotAuthorized},),
+ (None, {1: actions.plugins.toolkit.NotAuthorized, 2: actions.plugins.toolkit.ObjectNotFound},),
+ ({}, {}, [1]),
+ ({'user': 'fiware'}, {}, [3, 2]),
+ (None, {1: actions.plugins.toolkit.NotAuthorized}, [2]),
+ ({}, {1: actions.plugins.toolkit.NotAuthorized, 2: actions.plugins.toolkit.ObjectNotFound}, [1, 3]),
+ ])
+ def test_acquisitions_list(self, data_dict, package_errors={}, deleted_packages=[]):
+
+ pkgs_ids = [0, 1, 2, 3]
+ user = 'example_user_test'
+ actions.plugins.toolkit.c.user = user
+
+ # get_action mock
+ default_package = {'pkg_id': 0, 'test': 'ok', 'res': 'ta'}
+
+ def _package_show(context, data_dict):
+ if data_dict['id'] in package_errors:
+ raise package_errors[data_dict['id']]('ERROR')
+ else:
+ pkg = default_package.copy()
+ pkg['pkg_id'] = data_dict['id']
+ pkg['state'] = 'deleted' if data_dict['id'] in deleted_packages else 'active'
+ return pkg
+
+ package_show = MagicMock(side_effect=_package_show)
+ actions.plugins.toolkit.get_action.return_value = package_show
+
+ # query mock
+ query_res = []
+ for i in pkgs_ids:
+ pkg = MagicMock()
+ pkg.package_id = i
+ pkg.user_name = user
+ query_res.append(pkg)
+
+ actions.db.AllowedUser.get = MagicMock(return_value=query_res)
+
+ # Context
+ context = {
+ 'model': MagicMock(),
+ 'user': 'default_user'
+ }
+
+ # Call the function
+ result = actions.acquisitions_list(context, data_dict)
+
+ # Asset that check_access has been called
+ actions.plugins.toolkit.chec_access(actions.constants.ACQUISITIONS_LIST, context, data_dict)
+
+ # Check that the database has been initialized properly
+ actions.db.init_db.assert_called_once_with(context['model'])
+
+ # Set expected user
+ expected_user = data_dict['user'] if data_dict is not None and 'user' in data_dict else context['user']
+
+ # Query called correctry
+ actions.db.AllowedUser.get.assert_called_once_with(user_name=expected_user)
+
+ # Assert that the package_show has been called properly
+ self.assertEquals(len(pkgs_ids), package_show.call_count)
+ for i in pkgs_ids:
+ package_show.assert_any_call(context, {'id': i})
+
+ # Check that the template receives the correct datasets
+ expected_acquired_datasets = []
+ for i in pkgs_ids:
+ if i not in package_errors and i not in deleted_packages:
+ pkg = default_package.copy()
+ pkg['pkg_id'] = i
+ pkg['state'] = 'deleted' if i in deleted_packages else 'active'
+ expected_acquired_datasets.append(pkg)
+
+ self.assertEquals(expected_acquired_datasets, result)
diff --git a/ckanext/privatedatasets/tests/test_auth.py b/ckanext/privatedatasets/tests/test_auth.py
index 19d32d1..d7c1978 100644
--- a/ckanext/privatedatasets/tests/test_auth.py
+++ b/ckanext/privatedatasets/tests/test_auth.py
@@ -249,4 +249,12 @@ class AuthTest(unittest.TestCase):
self.assertEquals(authorized_pkg, result['success'])
def test_package_acquired(self):
- self.assertTrue(auth.package_acquired({}, {}))
+ self.assertTrue(auth.package_acquired({}, {})['success'])
+
+ @parameterized.expand([
+ ({'user': 'user_1'}, {'user': 'user_1'}, True),
+ ({'user': 'user_2'}, {'user': 'user_1'}, False),
+ ])
+ def test_acquisitions_list(self, context, data_dict, expected_result):
+ self.assertEquals(expected_result, auth.acquisitions_list(context, data_dict)['success'])
+
diff --git a/ckanext/privatedatasets/tests/test_controller_ui.py b/ckanext/privatedatasets/tests/test_controller_ui.py
index b5c61d6..6b2cc1e 100644
--- a/ckanext/privatedatasets/tests/test_controller_ui.py
+++ b/ckanext/privatedatasets/tests/test_controller_ui.py
@@ -38,9 +38,6 @@ class UIControllerTest(unittest.TestCase):
self._model = controller.model
controller.model = MagicMock()
- self._db = controller.db
- controller.db = MagicMock()
-
# Set exceptions
controller.plugins.toolkit.ObjectNotFound = self._plugins.toolkit.ObjectNotFound
controller.plugins.toolkit.NotAuthorized = self._plugins.toolkit.NotAuthorized
@@ -49,7 +46,6 @@ class UIControllerTest(unittest.TestCase):
# Unmock
controller.plugins = self._plugins
controller.model = self._model
- controller.db = self._db
@parameterized.expand([
(controller.plugins.toolkit.ObjectNotFound, 404),
@@ -74,62 +70,26 @@ class UIControllerTest(unittest.TestCase):
user_show.assert_called_once_with(expected_context, {'user_obj': controller.plugins.toolkit.c.userobj})
controller.plugins.toolkit.abort.assert_called_once_with(expected_status, ANY)
- @parameterized.expand([
- ({},),
- ({2: controller.plugins.toolkit.ObjectNotFound},),
- ({1: controller.plugins.toolkit.NotAuthorized},),
- ({1: controller.plugins.toolkit.NotAuthorized, 2: controller.plugins.toolkit.ObjectNotFound},),
- ({}, [1]),
- ({}, [3, 2]),
- ({1: controller.plugins.toolkit.NotAuthorized}, [2]),
- ({1: controller.plugins.toolkit.NotAuthorized, 2: controller.plugins.toolkit.ObjectNotFound}, [1, 3]),
- ])
- def test_no_error_loading_users(self, package_errors={}, deleted_packages=[]):
+ def test_no_error_loading_users(self):
- pkgs_ids = [0, 1, 2, 3]
user = 'example_user_test'
controller.plugins.toolkit.c.user = user
- # get_action mock
- default_package = {'pkg_id': 0, 'test': 'ok', 'res': 'ta'}
-
- def _package_show(context, data_dict):
- if data_dict['id'] in package_errors:
- raise package_errors[data_dict['id']]('ERROR')
- else:
- pkg = default_package.copy()
- pkg['pkg_id'] = data_dict['id']
- pkg['state'] = 'deleted' if data_dict['id'] in deleted_packages else 'active'
- return pkg
-
- user_dict = {'user_name': 'test', 'another_val': 'example value'}
- package_show = MagicMock(side_effect=_package_show)
- user_show = MagicMock(return_value=user_dict.copy())
-
+ # actions
+ default_user = {'user_name': 'test', 'another_val': 'example value'}
+ user_show = MagicMock(return_value=default_user)
+ acquisitions_list = MagicMock()
def _get_action(action):
- if action == 'package_show':
- return package_show
- elif action == 'user_show':
+ if action == 'user_show':
return user_show
+ else:
+ return acquisitions_list
controller.plugins.toolkit.get_action = MagicMock(side_effect=_get_action)
- # query mock
- query_res = []
- for i in pkgs_ids:
- pkg = MagicMock()
- pkg.package_id = i
- pkg.user_name = user
- query_res.append(pkg)
-
- controller.db.AllowedUser.get = MagicMock(return_value=query_res)
-
# Call the function
returned = self.instanceUI.user_acquired_datasets()
- # Check that the database has been initialized properly
- controller.db.init_db.assert_called_once_with(controller.model)
-
# User_show called correctly
expected_context = {
'model': controller.model,
@@ -140,24 +100,10 @@ class UIControllerTest(unittest.TestCase):
user_show.assert_called_once_with(expected_context, {'user_obj': controller.plugins.toolkit.c.userobj})
# Query called correctry
- controller.db.AllowedUser.get.assert_called_once_with(user_name=user)
-
- # Assert that the package_show has been called properly
- self.assertEquals(len(pkgs_ids), package_show.call_count)
- for i in pkgs_ids:
- package_show.assert_any_call(expected_context, {'id': i})
-
- # Check that the template receives the correct datasets
- expected_user_dict = user_dict.copy()
- expected_user_dict['acquired_datasets'] = []
- for i in pkgs_ids:
- if i not in package_errors and i not in deleted_packages:
- pkg = default_package.copy()
- pkg['pkg_id'] = i
- pkg['state'] = 'deleted' if i in deleted_packages else 'active'
- expected_user_dict['acquired_datasets'].append(pkg)
-
- self.assertEquals(expected_user_dict, controller.plugins.toolkit.c.user_dict)
+ expected_user = default_user.copy()
+ expected_user['acquired_datasets'] = acquisitions_list.return_value
+ acquisitions_list.assert_called_with(expected_context, None)
+ self.assertEquals(expected_user, controller.plugins.toolkit.c.user_dict)
# Check that the render method has been called and that its result has been returned
self.assertEquals(controller.plugins.toolkit.render.return_value, returned)
diff --git a/ckanext/privatedatasets/tests/test_plugin.py b/ckanext/privatedatasets/tests/test_plugin.py
index b32f696..fd07cbc 100644
--- a/ckanext/privatedatasets/tests/test_plugin.py
+++ b/ckanext/privatedatasets/tests/test_plugin.py
@@ -58,10 +58,11 @@ class PluginTest(unittest.TestCase):
self.assertTrue(interface.implemented_by(plugin.PrivateDatasets))
@parameterized.expand([
- ('package_show', plugin.auth.package_show),
- ('package_update', plugin.auth.package_update),
- ('package_show', plugin.auth.package_show),
- ('package_acquired', plugin.auth.package_acquired)
+ ('package_show', plugin.auth.package_show),
+ ('package_update', plugin.auth.package_update),
+ ('package_show', plugin.auth.package_show),
+ ('package_acquired', plugin.auth.package_acquired),
+ ('acquisitions_list', plugin.auth.acquisitions_list)
])
def test_auth_function(self, function_name, expected_function):
auth_functions = self.privateDatasets.get_auth_functions()
@@ -87,7 +88,8 @@ class PluginTest(unittest.TestCase):
action='user_acquired_datasets', conditions=dict(method=['GET']))
@parameterized.expand([
- ('package_acquired', plugin.actions.package_acquired)
+ ('package_acquired', plugin.actions.package_acquired),
+ ('acquisitions_list', plugin.actions.acquisitions_list)
])
def test_actions_function(self, function_name, expected_function):
actions = self.privateDatasets.get_actions()