Skip to content

Commit

Permalink
[#943] Add tutorial section on error handling in extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean Hammond committed Jun 28, 2013
1 parent 5b7b0b4 commit cccf5d2
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 0 deletions.
55 changes: 55 additions & 0 deletions ckanext/examples/iauthfunctions/plugin_4.py
@@ -0,0 +1,55 @@
import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit

# FIXME: Shouldn't have to import thise here (it's needed for the
# Invalid exception class below).
import ckan.lib.navl.dictization_functions as df


def group_create(context, data_dict=None):

# Get the user name of the logged-in user.
user_name = context['user']

# Get a list of the members of the 'curators' group.
try:
members = toolkit.get_action('member_list')(
data_dict={'id': 'curators', 'object_type': 'user'})
except toolkit.ObjectNotFound:
# The curators group doesn't exist.
return {'success': False,
'msg': "The curators groups doesn't exist, so only sysadmins "
"are authorized to create groups."}

# 'members' is a list of (user_id, object_type, capacity) tuples, we're
# only interested in the user_ids.
member_ids = [member_tuple[0] for member_tuple in members]

# FIXME: An extension shouldn't have to do this.
context['session'] = context['model'].Session

# We have the logged-in user's user name, get their user id.
convert_user_name_or_id_to_id = toolkit.get_converter(
'convert_user_name_or_id_to_id')
try:
user_id = convert_user_name_or_id_to_id(user_name, context)
except df.Invalid:
# The user doesn't exist (e.g. they're not logged-in).
return {'success': False,
'msg': 'You must be logged-in as a member of the curators '
'group to create new groups.'}

# Finally, we can test whether the user is a member of the curators group.
if user_id in member_ids:
return {'success': True}
else:
return {'success': False,
'msg': 'Only curators are allowed to create groups'}


class ExampleIAuthFunctionsPlugin(plugins.SingletonPlugin):
plugins.implements(plugins.IAuthFunctions, inherit=False)

def get_auth_functions(self):
return {'group_create': group_create}

2 changes: 2 additions & 0 deletions doc/api.rst
Expand Up @@ -231,6 +231,8 @@ different sites running different versions of CKAN, the result of an API
request that doesn't specify the API version number cannot be relied on.


.. _api authentication:

---------------------------
Authentication and API Keys
---------------------------
Expand Down
67 changes: 67 additions & 0 deletions doc/writing-extensions.rst
Expand Up @@ -431,6 +431,73 @@ group, and allow or refuse the action:
:end-before: class ExampleIAuthFunctionsPlugin(plugins.SingletonPlugin):


Exception handling
==================

There are two bugs in our ``ExampleIAuthFunctionsPlugin3`` class that need to
be fixed using exception handling. First, the class will crash if the site does
not have a group named ``curators``.

Try visiting the ``/group`` page in CKAN with our ``example_iauthfunctions_3``
plugin activated in your CKAN config file and with no ``curators`` group in
your site. If you have ``debug = false`` in your CKAN config file, you'll see
something like this in your browser::

Error 500

Server Error

An internal server error occurred

If you have ``debug = true`` in your CKAN config file, then you'll see a
traceback page with details about the crash.

You'll also get a ``500 Server Error`` if you try to create a group using the
``group_create`` API action.

To handle this situation where the site has no curators group without crashing,
we'll have to handle the exceptio that CKAN's ``member_list`` function raises
when it's asked to list the members of a group that doesn't exist:

.. literalinclude:: ../ckanext/examples/iauthfunctions/plugin_4.py
:start-after: # Get a list of the members of the 'curators' group.
:end-before: # 'members' is a list of (user_id, object_type, capacity) tuples, we're

With these ``try`` and ``except`` clauses added, we should be able to load the
``/group`` page and add groups, even if there isn't already a group called
curators.

Second, ``ExampleIAuthFunctionsPlugin3`` will crash if a user who is not
logged-in tries to create a group. If you logout of CKAN, and then visit
``/group/new`` you'll see another ``500 Server Error``. You'll also get this
error if you post to the :func:`group_create` API action without
:ref:`providing an API key <api authentication>`.

When the user isn't logged in, ``context['user']`` contains the user's IP
address instead of a user name::

{'model': <module 'ckan.model' from ...>,
'user': u'127.0.0.1'}

When we pass this IP address as the user name to
:func:`convert_user_name_or_id_to_id`, the converter function will raise an
exception because no user with that user name exists. We need to handle that
exception as well:

.. literalinclude:: ../ckanext/examples/iauthfunctions/plugin_4.py
:start-after: # We have the logged-in user's user name, get their user id.
:end-before: # Finally, we can test whether the user is a member of the curators group.


We're done!
===========

Here's our final, working ``plugin_4.py`` module in full:

.. literalinclude:: ../ckanext/examples/iauthfunctions/plugin_4.py



Troubleshooting
===============

Expand Down
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -129,6 +129,7 @@
example_iauthfunctions_1=ckanext.examples.iauthfunctions.plugin_1:ExampleIAuthFunctionsPlugin
example_iauthfunctions_2=ckanext.examples.iauthfunctions.plugin_2:ExampleIAuthFunctionsPlugin
example_iauthfunctions_3=ckanext.examples.iauthfunctions.plugin_3:ExampleIAuthFunctionsPlugin
example_iauthfunctions_4=ckanext.examples.iauthfunctions.plugin_4:ExampleIAuthFunctionsPlugin
[ckan.system_plugins]
domain_object_mods = ckan.model.modification:DomainObjectModificationExtension
Expand Down

0 comments on commit cccf5d2

Please sign in to comment.