<img src="https://raw.githubusercontent.com/NaturalHistoryMuseum/ckantools/main/.github/nhm-logo.svg" align="left" width="150px" height="100px" hspace="40"/>

# ckantools

[![CKAN](https://img.shields.io/badge/ckan-2.9.5-orange.svg?style=flat-square)](https://github.com/ckan/ckan)
[![Python](https://img.shields.io/badge/python-3.6%20%7C%203.7%20%7C%203.8-blue.svg?style=flat-square)](https://www.python.org/)

_Utilities and common methods for CKAN extensions._


# Overview

A collection of methods, decorators, and anything else that might be useful.

# Installation

```shell
pip install ckantools
```

# Usage

## Actions

Use the `@action` decorator to add actions:
```python
# logic/actions/module_name.py

from ckantools.decorators import action

schema = {
        'parameter_1': [not_missing, str],
        'parameter_2': [ignore_missing, int_validator]
    }

helptext = 'This action only exists as an example, so does not actually anything.'

@action(schema, helptext)
def example_action(parameter_1, parameter_2):
    # ...
```

And then load the action(s) in `plugin.py`:
```python
# plugin.py

from .logic.actions import module_name
from ckantools.loaders import create_actions
from ckan.plugins import implements, interfaces, SingletonPlugin

class ExamplePlugin(SingletonPlugin):
    implements(interfaces.IActions)
    
    # IActions
    def get_actions(self):
        return create_actions(module_name)
```

Main benefits to using the decorator:
- automatically calls relevant auth function
- injects items defined in schema as function args
- allows you to define long or complex schemas and helptexts without cluttering up code and/or affecting readability
- neater and easier to maintain than having to list out all of the actions you want to load, e.g.
  ```python
        ## IActions
  def get_actions(self):
    return {
        'example_action': module_name.example_action,
        'other_action': module_name.other_action,
        'other_other_action': module_name.other_other_action,
        # etc
    }
  ```

## Auth

Loading auth functions is similar to actions, i.e. use the `@auth` decorator.

The decorator has three args, all of which are optional:
- `proxy`: the name of an existing auth function to call that function first
- `keymapping`: if the keys are different between this auth function and the proxy auth function, use this to rename them
- `anon`: boolean, if true, apply the `toolkit.auth_allow_anonymous_access` decorator.

```python
# logic/auth/module_name.py

from ckantools.decorators import auth

# all of the args are optional
@auth(anon=True)
def example_action(context, data_dict):
    # no proxy
    # anonymous access is allowed
    # then the custom auth logic:
    return {'success': data_dict.get('parameter_2') == 'carrots'}

# with args
@auth('example_action', {'param_1': 'parameter_2'})
def other_action(context, data_dict):
    # checks access to example_action first
    # the arg param_1 from this action is the same as parameter_2 in example_action (not all args/parameters have to be mapped, just the relevant ones)
    # anonymous access is not allowed
    # if it passes all that:
    return {'success': True}
```

The auth functions can then be loaded in `plugin.py`:
```python
# plugin.py

from .logic.auth import module_name
from ckantools.loaders import create_auth
from ckan.plugins import implements, interfaces, SingletonPlugin

class ExamplePlugin(SingletonPlugin):
    implements(interfaces.IActions)
    
    # IAuthFunctions
    def get_auth_functions(self):
        return create_auth(module_name)
```

Main benefits to using the decorator:
- reduces repetition of complex auth logic
- as with the action decorator, it's neater and easier to maintain than having to list out all of the auth functions to load
