Repository: ckan/ckan

Issue:
Chained actions, auth functions and helpers are not documented
**CKAN version**
2.7, 2,8, 2.9, master

**Describe the bug**

Chained actions are missing docstring completely:
https://github.com/ckan/ckan/blob/e89ca125dc68ddb9fe9bad68a401404146ba90c7/ckan/logic/__init__.py#L334-L336
There is a mention of chained actions within get_actions:
https://github.com/ckan/ckan/blob/e89ca125dc68ddb9fe9bad68a401404146ba90c7/ckan/plugins/interfaces.py#L922-L935

Chained auth functions have minimal docstring:
https://github.com/ckan/ckan/blob/e89ca125dc68ddb9fe9bad68a401404146ba90c7/ckan/logic/__init__.py#L634-L639

Chained helpers have minimal docstring:
https://github.com/ckan/ckan/blob/e89ca125dc68ddb9fe9bad68a401404146ba90c7/ckan/lib/helpers.py#L141-L145

Changelogs have minor mentions to them. `chained_action` was added in 2.7 https://github.com/ckan/ckan/pull/3494 and 2.9 docs have a mention to them:
![image](https://user-images.githubusercontent.com/830663/122172865-df78a800-ce89-11eb-852f-966809ddc474.png)

`chained_auth_function` was added in 2.8 https://github.com/ckan/ckan/pull/3995 and docs have a small mention of them:
![image](https://user-images.githubusercontent.com/830663/122173107-19e24500-ce8a-11eb-92d1-46f2967480e3.png)

`chained_helper` is only in master https://github.com/ckan/ckan/pull/5337 and are not mentioned in the changelog. 

There is a minor example how to use these in:
https://github.com/ckan/ckan/blob/e89ca125dc68ddb9fe9bad68a401404146ba90c7/ckanext/chained_functions/plugin.py#L32-L46



Discussion:

Resolving patch:
diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py
--- a/ckan/lib/helpers.py
+++ b/ckan/lib/helpers.py
@@ -139,8 +139,36 @@ def _is_chained_helper(func):
 
 
 def chained_helper(func):
-    """Decorator function allowing helper functions to be chained.
-    """
+    '''Decorator function allowing helper functions to be chained.
+
+    This chain starts with the first chained helper to be registered and
+    ends with the original helper (or a non-chained plugin override
+    version). Chained helpers must accept an extra parameter,
+    specifically the next helper in the chain, for example::
+
+            helper(next_helper, *args, **kwargs).
+
+    The chained helper function may call the next_helper function,
+    optionally passing different values, handling exceptions,
+    returning different values and/or raising different exceptions
+    to the caller.
+
+    Usage::
+
+        from ckan.plugins.toolkit import chained_helper
+
+        @chained_helper
+        def ckan_version(next_func, **kw):
+
+            return next_func(**kw)
+
+    :param func: chained helper function
+    :type func: callable
+
+    :returns: chained helper function
+    :rtype: callable
+
+    '''
     func.chained_helper = True
     return func
 
diff --git a/ckan/logic/__init__.py b/ckan/logic/__init__.py
--- a/ckan/logic/__init__.py
+++ b/ckan/logic/__init__.py
@@ -332,6 +332,33 @@ def clear_actions_cache():
 
 
 def chained_action(func):
+    '''Decorator function allowing action function to be chained.
+
+    This allows a plugin to modify the behaviour of an existing action
+    function. A Chained action function must be defined as
+    ``action_function(original_action, context, data_dict)`` where the
+    first parameter will be set to the action function in the next plugin
+    or in core ckan. The chained action may call the original_action
+    function, optionally passing different values, handling exceptions,
+    returning different values and/or raising different exceptions
+    to the caller.
+
+    Usage::
+
+        from ckan.plugins.toolkit import chained_action
+
+        @chained_action
+        @side_effect_free
+        def package_search(original_action, context, data_dict):
+            return original_action(context, data_dict)
+
+    :param func: chained action function
+    :type func: callable
+
+    :returns: chained action function
+    :rtype: callable
+
+    '''
     func.chained_action = True
     return func
 
@@ -634,6 +661,33 @@ def auth_disallow_anonymous_access(action):
 def chained_auth_function(func):
     '''
     Decorator function allowing authentication functions to be chained.
+
+    This chain starts with the last chained auth function to be registered and
+    ends with the original auth function (or a non-chained plugin override
+    version). Chained auth functions must accept an extra parameter,
+    specifically the next auth function in the chain, for example::
+
+        auth_function(next_auth, context, data_dict).
+
+    The chained auth function may call the next_auth function, optionally
+    passing different values, handling exceptions, returning different
+    values and/or raising different exceptions to the caller.
+
+    Usage::
+
+        from ckan.plugins.toolkit import chained_auth_function
+
+        @chained_auth_function
+        @auth_allow_anonymous_access
+        def user_show(next_auth, context, data_dict=None):
+            return next_auth(context, data_dict)
+
+    :param func: chained authentication function
+    :type func: callable
+
+    :returns: chained authentication function
+    :rtype: callable
+
     '''
     func.chained_auth_function = True
     return func


Test patch: