Move the notification callback to an action
This commit is contained in:
parent
4bdd121dd9
commit
9a49f06fff
10
README.md
10
README.md
|
@ -21,14 +21,12 @@ Since each service can send notifications in a different way, the extension allo
|
|||
|
||||
If you want to create your own parser, you have to:
|
||||
|
||||
1. Create a class with a method called `parse_notification`
|
||||
2. Import `request` from `ckan.common` in order to be able to read the notification: `from ckan.common import request`.
|
||||
3. Parse the notification as you like. You can read the body by accesing `request.body`.
|
||||
4. Return a dictionary with the following structure. The `errors` field contains the list of errors arised when the notification was parsed while the `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`). If the `error` field is present and it is **not** empty, the `users_datasets` field will **not** be processed.
|
||||
1. Create a class with a method called `parse_notification`. This method will recieve one argument that will include the notification body.
|
||||
2. Parse the notification as you like. You can raise a CKAN's default exception (`DataError`, `ValidationError`, `ObjectNotFound`, `NotAuthorized`, `ValidationError`, `SearchError`, `SearchQueryError` or `SearchIndexError`) if you find an error parsing the notification.
|
||||
3. Return a dictionary with the structure attached below. The `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`).
|
||||
|
||||
```
|
||||
{'errors': ['...', '...', '...']
|
||||
'users_datasets': [{'user': 'user_name', 'datasets': ['ds1', 'ds2', ...]},
|
||||
{'users_datasets': [{'user': 'user_name', 'datasets': ['ds1', 'ds2', ...]},
|
||||
{'user': 'user_name2', 'datasets': ['ds1', 'ds4', ...] }]}
|
||||
```
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import ckan.plugins as plugins
|
||||
import ckanext.privatedatasets.constants as constants
|
||||
import importlib
|
||||
import logging
|
||||
import pylons.config as config
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
PARSER_CONFIG_PROP = 'ckan.privatedatasets.parser'
|
||||
|
||||
|
||||
def package_adquired(context, request_data):
|
||||
|
||||
log.info('Notification Request received')
|
||||
|
||||
# Get the parser from the configuration
|
||||
class_path = config.get(PARSER_CONFIG_PROP, '')
|
||||
|
||||
if class_path != '':
|
||||
try:
|
||||
class_package = class_path.split(':')[0]
|
||||
class_name = class_path.split(':')[1]
|
||||
parser_cls = getattr(importlib.import_module(class_package), class_name)
|
||||
parser = parser_cls()
|
||||
except Exception as e:
|
||||
raise plugins.toolkit.ValidationError({'message': '%s: %s' % (type(e).__name__, str(e))})
|
||||
else:
|
||||
raise plugins.toolkit.ValidationError({'message': '%s not configured' % PARSER_CONFIG_PROP})
|
||||
|
||||
# Introduce the users into the datasets
|
||||
# Expected result: {'errors': ["...", "...", ...]
|
||||
# 'users_datasets': [{'user': 'user_name', 'datasets': ['ds1', 'ds2', ...]}, ...]}
|
||||
warns = []
|
||||
|
||||
# Parse the result using the parser set in the configuration
|
||||
result = parser.parse_notification(request_data)
|
||||
|
||||
for user_info in result['users_datasets']:
|
||||
for dataset_id in user_info['datasets']:
|
||||
try:
|
||||
|
||||
dataset = plugins.toolkit.get_action('package_show')({'ignore_auth': True, constants.CONTEXT_CALLBACK: True}, {'id': dataset_id})
|
||||
|
||||
# Create the array
|
||||
if constants.ALLOWED_USERS not in dataset:
|
||||
dataset[constants.ALLOWED_USERS] = []
|
||||
|
||||
# Add the user only if he/she is not in the list
|
||||
if user_info['user'] not in dataset[constants.ALLOWED_USERS]:
|
||||
dataset[constants.ALLOWED_USERS].append(user_info['user'])
|
||||
plugins.toolkit.get_action('package_update')({'ignore_auth': True}, dataset)
|
||||
else:
|
||||
log.warn('The user %s is already allowed to access the %s dataset' % (user_info['user'], dataset_id))
|
||||
|
||||
except plugins.toolkit.ObjectNotFound:
|
||||
# If a dataset does not exist in the instance, an error message will be returned to the user.
|
||||
# However the process won't stop and the process will continue with the remaining datasets.
|
||||
log.warn('Dataset %s was not found in this instance' % dataset_id)
|
||||
warns.append('Dataset %s was not found in this instance' % dataset_id)
|
||||
except plugins.toolkit.ValidationError as e:
|
||||
# Some datasets does not allow to introduce the list of allowed users since this property is
|
||||
# only valid for private datasets outside an organization. In this case, a wanr will return
|
||||
# but the process will continue
|
||||
warns.append('Dataset %s: %s' % (dataset_id, e.error_dict[constants.ALLOWED_USERS][0]))
|
||||
|
||||
# Return warnings that inform about non-existing datasets
|
||||
if len(warns) > 0:
|
||||
return {'warns': warns}
|
|
@ -1,94 +0,0 @@
|
|||
import ckan.lib.base as base
|
||||
import ckan.lib.helpers as helpers
|
||||
import ckan.model as model
|
||||
import ckan.plugins as plugins
|
||||
import ckanext.privatedatasets.constants as constants
|
||||
import ckanext.privatedatasets.db as db
|
||||
import importlib
|
||||
import logging
|
||||
import pylons.config as config
|
||||
|
||||
from ckan.common import response
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
PARSER_CONFIG_PROP = 'ckan.privatedatasets.parser'
|
||||
|
||||
|
||||
######################################################################
|
||||
############################ API CONTROLLER ##########################
|
||||
######################################################################
|
||||
|
||||
class AdquiredDatasetsControllerAPI(base.BaseController):
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
# avoid status_code_redirect intercepting error responses
|
||||
environ['pylons.status_code_redirect'] = True
|
||||
return base.BaseController.__call__(self, environ, start_response)
|
||||
|
||||
def add_users(self):
|
||||
|
||||
log.info('Notification Request received')
|
||||
|
||||
if db.package_allowed_users_table is None:
|
||||
db.init_db(model)
|
||||
|
||||
# Get the parser from the configuration
|
||||
class_path = config.get(PARSER_CONFIG_PROP, '')
|
||||
|
||||
if class_path != '':
|
||||
try:
|
||||
class_package = class_path.split(':')[0]
|
||||
class_name = class_path.split(':')[1]
|
||||
parser = getattr(importlib.import_module(class_package), class_name)
|
||||
# Parse the result using the parser set in the configuration
|
||||
result = parser().parse_notification()
|
||||
except Exception as e:
|
||||
result = {'errors': [type(e).__name__ + ': ' + str(e)]}
|
||||
else:
|
||||
result = {'errors': ['%s not configured' % PARSER_CONFIG_PROP]}
|
||||
|
||||
# Introduce the users into the datasets
|
||||
# Expected result: {'errors': ["...", "...", ...]
|
||||
# 'users_datasets': [{'user': 'user_name', 'datasets': ['ds1', 'ds2', ...]}, ...]}
|
||||
warns = []
|
||||
|
||||
if 'errors' in result and len(result['errors']) > 0:
|
||||
log.warn('Errors arised parsing the request: ' + str(result['errors']))
|
||||
response.status_int = 400
|
||||
return helpers.json.dumps({'errors': result['errors']})
|
||||
elif 'users_datasets' in result:
|
||||
for user_info in result['users_datasets']:
|
||||
for dataset_id in user_info['datasets']:
|
||||
try:
|
||||
|
||||
dataset = plugins.toolkit.get_action('package_show')({'ignore_auth': True, constants.CONTEXT_CALLBACK: True}, {'id': dataset_id})
|
||||
|
||||
# Create the array
|
||||
if constants.ALLOWED_USERS not in dataset:
|
||||
dataset[constants.ALLOWED_USERS] = []
|
||||
|
||||
# Add the user only if he/she is not in the list
|
||||
if user_info['user'] not in dataset[constants.ALLOWED_USERS]:
|
||||
dataset[constants.ALLOWED_USERS].append(user_info['user'])
|
||||
plugins.toolkit.get_action('package_update')({'ignore_auth': True}, dataset)
|
||||
else:
|
||||
log.warn('The user %s is already allowed to access the %s dataset' % (user_info['user'], dataset_id))
|
||||
|
||||
except plugins.toolkit.ObjectNotFound:
|
||||
# If a dataset does not exist in the instance, an error message will be returned to the user.
|
||||
# However the process won't stop and the process will continue with the remaining datasets.
|
||||
log.warn('Dataset %s was not found in this instance' % dataset_id)
|
||||
warns.append('Dataset %s was not found in this instance' % dataset_id)
|
||||
except plugins.toolkit.ValidationError as e:
|
||||
# Some datasets does not allow to introduce the list of allowed users since this property is
|
||||
# only valid for private datasets outside an organization. In this case, a wanr will return
|
||||
# but the process will continue
|
||||
warns.append('Dataset %s: %s' % (dataset_id, e.error_dict[constants.ALLOWED_USERS][0]))
|
||||
|
||||
# Commit the changes
|
||||
model.Session.commit()
|
||||
|
||||
# Return warnings that inform about non-existing datasets
|
||||
if len(warns) > 0:
|
||||
return helpers.json.dumps({'warns': warns})
|
|
@ -1,4 +1,4 @@
|
|||
import ckan.lib.helpers as helpers
|
||||
import ckan.plugins.toolkit as tk
|
||||
import re
|
||||
|
||||
from urlparse import urlparse
|
||||
|
@ -7,16 +7,14 @@ from ckan.common import request
|
|||
|
||||
class FiWareNotificationParser(object):
|
||||
|
||||
def parse_notification(self):
|
||||
def parse_notification(self, request_data):
|
||||
|
||||
my_host = request.host
|
||||
|
||||
# Parse the body
|
||||
content = helpers.json.loads(request.body, encoding='utf-8')
|
||||
resources = content['resources']
|
||||
user_name = content['customer_name']
|
||||
resources = request_data['resources']
|
||||
user_name = request_data['customer_name']
|
||||
datasets = []
|
||||
errors = []
|
||||
|
||||
for resource in resources:
|
||||
if 'url' in resource:
|
||||
|
@ -27,8 +25,7 @@ class FiWareNotificationParser(object):
|
|||
if parsed_url.netloc == my_host:
|
||||
datasets.append(dataset_name[0])
|
||||
else:
|
||||
errors.append('Dataset %s is associated with the CKAN instance located at %s' % (dataset_name[0], parsed_url.netloc))
|
||||
raise tk.ValidationError({'message': 'Dataset %s is associated with the CKAN instance located at %s'
|
||||
% (dataset_name[0], parsed_url.netloc)})
|
||||
|
||||
return {'errors': errors,
|
||||
'users_datasets': [{'user': user_name, 'datasets': datasets}]
|
||||
}
|
||||
return {'users_datasets': [{'user': user_name, 'datasets': datasets}]}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import ckan.plugins as p
|
||||
import ckan.plugins.toolkit as tk
|
||||
import auth
|
||||
import actions
|
||||
import constants
|
||||
import converters_validators as conv_val
|
||||
import db
|
||||
|
@ -13,6 +14,7 @@ class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm):
|
|||
p.implements(p.IAuthFunctions)
|
||||
p.implements(p.IConfigurer)
|
||||
p.implements(p.IRoutes, inherit=True)
|
||||
p.implements(p.IActions)
|
||||
p.implements(p.IPackageController)
|
||||
p.implements(p.ITemplateHelpers)
|
||||
|
||||
|
@ -95,20 +97,24 @@ class PrivateDatasets(p.SingletonPlugin, tk.DefaultDatasetForm):
|
|||
tk.add_resource('fanstatic', 'privatedatasets')
|
||||
|
||||
######################################################################
|
||||
############################### ROUTES ###############################
|
||||
############################## IROUTES ###############################
|
||||
######################################################################
|
||||
|
||||
def after_map(self, m):
|
||||
# DataSet adquired notification
|
||||
m.connect('/dataset_adquired',
|
||||
controller='ckanext.privatedatasets.controllers.api_controller:AdquiredDatasetsControllerAPI',
|
||||
action='add_users', conditions=dict(method=['POST']))
|
||||
m.connect('user_adquired_datasets', '/dashboad/adquired', ckan_icon='shopping-cart',
|
||||
controller='ckanext.privatedatasets.controllers.ui_controller:AdquiredDatasetsControllerUI',
|
||||
action='user_adquired_datasets', conditions=dict(method=['GET']))
|
||||
|
||||
return m
|
||||
|
||||
######################################################################
|
||||
############################## IACTIONS ##############################
|
||||
######################################################################
|
||||
|
||||
def get_actions(self):
|
||||
return {'package_adquired': actions.package_adquired}
|
||||
|
||||
######################################################################
|
||||
######################### IPACKAGECONTROLLER #########################
|
||||
######################################################################
|
||||
|
|
Loading…
Reference in New Issue